美文网首页
JAVA 隐藏类(Hidden Classes)

JAVA 隐藏类(Hidden Classes)

作者: 一万年不是尽头 | 来源:发表于2021-03-04 13:44 被阅读0次

    简介

    来自JEP371
    隐藏类,其他类的字节码不能直接使用的类。隐藏类适用于在运行时生成类并通过反射间接使用它们。隐藏类可以定义为访问控制嵌套的成员,并且可以独立于其他类进行卸载。

    原因

    近几年,产生很多基于jvm的动态语言,例如groovy,kotlin等,基本都是基于动态代理来实现动态语言的功能的,而像lambda表达式,也是在运行过程中传输一个字节码,而该字节码可以动态生成一个类并且实例化。

    这样就会在程序执行的过程中产生很多的类,二基于传统关于定于标准java类的api上看(ClassLoader::defineClassLookup::defineClass)这些动态生成的类根本无法被识别出来。这样这些动态类型很容易被发现,并且声明周期会变得很长。

    如果可以从标准API去定义一个可以隐藏且生命周期有限的类,那么肯定能够提高所有基于jvm的语言的实现效率,例如:

    • java.lang.reflect.Proxy可以定义隐藏类作为实现代理接口的代理类
    • java.lang.invoke.StringConcatFactory 可以生成隐藏类来保存常量连接方法
    • java.lang.invoke.LambdaMetaFactory 可以生成隐藏的nestmate类,以保存访问封闭变量的lambda主体
    • JavaScript引擎可以为从JavaScript程序转换的字节码生成隐藏类,因为当引擎不再使用这些类时,这些类将被卸载

    创建一个隐藏类

    • 普通类是通过调用创建的ClassLoader::defineClass

    • 隐藏类是通过调用创建的java.lang.invoke.MethodHandles.Lookup#defineHiddenClass
      具体的用法可以看jdk15中的java.lang.invoke.InnerClassLambdaMetafactory#generateInnerClass

    defineHiddenClass(byte[] bytes, boolean initialize, ClassOption... options)有三个参数

    • bytes 一个符合java虚拟机规范的字节码
    • initialize 如果为true,那么这个类会被初始化
    • options java类的类型详见java.lang.invoke.MethodHandles.Lookup.ClassOption

    隐藏类的使用

    Lookup::defineHiddenClass会返回一个Lookup对象,我们可以调用Lookup::lookupClass获取这个隐藏类的类型,隐藏类和普通的类一样使用,但是会有以下四点注意的地方:

    1. Class::getName 返回不是二进制名称的字符串

    2. Class::getCanonicalName返回值是null,表示示隐藏的类没有全限定名。(注意,Java语言中的匿名类对象该方法的返回值也是null。)

    3. 在隐藏类中声明的所有字段都是不可修改的,无论是使用Field::set还是其他修改字段的方法都会抛出IllegalAccessException(包括反射设置accessibleTrue时)

    4. 隐藏类对象不能被 instrumentation agents修改,也不能被JVM TI agents重新定义或转换。

    调试中跟踪隐藏类

    添加启动参数

    -XX:+UnlockDiagnosticVMOptions
    -XX:+ShowHiddenFrames
    

    有三个api可以获取隐藏类的堆栈信息

    Throwable::getStackTrace
    Thread::getStackTrace
    StackWalkerAPI
    

    相关文章

      网友评论

          本文标题:JAVA 隐藏类(Hidden Classes)

          本文链接:https://www.haomeiwen.com/subject/sbckqltx.html