代码之家  ›  专栏  ›  技术社区  ›  Louis Wasserman

MethodHandles.lookup().defineClass保留

  •  7
  • Louis Wasserman  · 技术社区  · 6 年前

    MethodHandles.Lookup.defineClass 在运行时从字节数组生成新类。

    在什么情况下可以对返回的类进行垃圾回收?它是否在与 Lookup Class 对象不再被引用?

    1 回复  |  直到 6 年前
        1
  •  6
  •   Holger    4 年前

    通过 MethodHandles.Lookup.defineClass创建的类与任何其他类一样在定义类加载程序中注册,并且可以像普通类一样通过名称引用,而不是 defineHiddenClass(…) ,可用于定义未在类加载器中注册的类,还可以访问查找类的 private 成员,类似于旧的非标准特性sun.misc.Unsafe.defineAnonymousClass

    • NESTMATE 允许访问lookup类的 private 成员及其嵌套成员(通常是内部类)

      你知道吗
    • 你知道吗

      以下是示例的改编版本:

      公共类LookupHiddenClass{
      var lookup=MethodHandles.lookup();
      lookup=lookup.defineHiddenClass((“\0\0\0005\0\26\11\0\11\0\12\10\0\13\12”
      +“\0\14\0\15\7\0\16\7\0\17\1\0\3foo\1\0\3()V\1\0\4Code\7\0\20\14\0\21\0\22\1\0”
      +“\27hello来自隐藏类\7\0\23\14\0\24\0\25\1\0\4Lazy\1\0\20java/”
      +“lang/Object\1\0\20java/lang/System\1\0\3out\1\0\25Ljava/io/PrintStream;”
      +“\1\0\23java/io/PrintStream\1\0\7println\1\0\25(Ljava/lang/String;)V\6\0英寸
      +"\0\4\0\5\0\0\0\0\0\1\0\11\0\6\0\7\0\1\0\10\0\0\0\25\0\2\0\0\0\0\0\11\0"
      + "\1\22\2\0\3±\0\0\0\0\0“).getBytes(StandardCharsets.ISO\u 8859\u 1),true);
      
      
      var r=新幻影参考<&燃气轮机(lookup.lookupClass(),q);
      
      查找=空;
      
      do System.gc();而(q.remove(1000)!=r) ;
      
      System.out.println(“收集的类”);
      }
      }
      
      来自隐藏类的hello
      

      在线试用

      此示例动态定义 Lazy 谁的班级 foo() 方法将打印 hello from dynamic class

      在类似HotSpot的jvm上,符号引用 在尝试调用 Lazy.foo() 则这将在动态定义的类中结束。对于渴望解析符号引用的jvm 懒惰的 被调用,因此 LinkageError 如果出现类似以下消息,将抛出重复的Lazy定义。


      引入JDK15 它可以用来定义未在类装入器中注册的类,也可以访问 private 查找类的成员,类似于旧的非标准功能 sun.misc.Unsafe.defineAnonymousClass

      行为由 ClassOption 参数。

      • NESTMATE 允许访问 私有的 查找类的成员及其嵌套成员(通常是内部类)

      • STRONG 防止类被卸载,直到类加载器变得不可访问,尽管我不知道为什么有人会想要这个。如果没有这个选项,一旦不存在对隐藏类的引用,它就可以被垃圾收集并卸载。

      public class LookupHiddenClass {
          public static void main(String[] args) throws Throwable {
              var lookup = MethodHandles.lookup();
              lookup = lookup.defineHiddenClass(("Êþº¾\0\0\0005\0\26\11\0\11\0\12\10\0\13\12"
              +"\0\14\0\15\7\0\16\7\0\17\1\0\3foo\1\0\3()V\1\0\4Code\7\0\20\14\0\21\0\22\1\0"
              +"\27hello from hidden class\7\0\23\14\0\24\0\25\1\0\4Lazy\1\0\20java/"
              +"lang/Object\1\0\20java/lang/System\1\0\3out\1\0\25Ljava/io/PrintStream;"
              +"\1\0\23java/io/PrintStream\1\0\7println\1\0\25(Ljava/lang/String;)V\6\0"
              +"\0\4\0\5\0\0\0\0\0\1\0\11\0\6\0\7\0\1\0\10\0\0\0\25\0\2\0\0\0\0\0\11²\0"
              + "\1\22\2¶\0\3±\0\0\0\0\0\0").getBytes(StandardCharsets.ISO_8859_1), true);
      
              lookup.findStatic(lookup.lookupClass(), "foo", MethodType.methodType(void.class))
                    .invokeExact();
      
              var q = new ReferenceQueue<Class<?>>();
              var r = new PhantomReference<>(lookup.lookupClass(), q);
      
              lookup = null;
      
              do System.gc(); while(q.remove(1000) != r);
      
              System.out.println("class collected");
          }
      }
      

      System.gc() 将执行实际的垃圾收集,也不会收集特定的对象,使用OpenJDKs默认配置可重复打印

      hello from hidden class
      class collected