gpt4 book ai didi

java - MethodHandles.lookup().defineClass保留

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:24:31 29 4
gpt4 key购买 nike

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

返回类在什么情况下可以被垃圾回收?它是否在与 Lookup 对象关联的类加载器的生命周期内保留,或者如果不再引用 Class 对象,它是否可以被垃圾收集?

最佳答案

通过 MethodHandles.Lookup.defineClass 创建的类像任何其他类一样在定义类加载器中注册,并且可以像普通类一样通过名称引用,这与 defineHiddenClass(...) 不同。与 JDK 15 一起引入(请参阅答案末尾)。在这些类被解析之前注册时,它们甚至可能会取代静态编译的类,如下例所示:

import java.lang.invoke.MethodHandles;
import java.nio.charset.StandardCharsets;

public class LookupDynamicClass {
public static void main(String[] args) throws IllegalAccessException {
MethodHandles.Lookup lookup = MethodHandles.lookup();
lookup.defineClass(("Êþº¾\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"
+"\30hello from dynamic 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));

Lazy.foo();
}
}
interface Lazy {
static void foo() {
}
}

Try it online

此示例动态定义了一个 Lazy 类,其 foo() 方法在被调用时将打印 hello from dynamic class

在像 HotSpot 这样的 JVM 上,符号引用“Lazy”被延迟解析,即在尝试调用 Lazy.foo() 时,这将最终进入动态定义的类。对于热切解析符号引用的 JVM,Lazy 类在调用 MethodHandles.Lookup.defineClass 时已经存在,因此,一个带有消息的 LinkageError像“尝试重复定义懒惰”将被抛出。

换句话说,这些动态生成的类与静态编译的类共享相同的 namespace (类加载上下文)。像普通类一样在类加载器中注册,它们只能在定义类加载器变得不可访问(包括其所有定义的类)时被垃圾收集,就像普通类一样。


引入了 JDK 15 defineHiddenClass(...)它可用于定义未在类加载器中注册的类,并且还能够访问查找类的 private 成员,类似于较旧的非标准功能 sun.misc.Unsafe.defineAnonymousClass

该行为由 ClassOption 控制参数。

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

  • 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() 将执行实际的垃圾收集,也不能保证它确实收集特定对象,但使用 OpenJDK 的默认配置它可以重复打印

hello from hidden class
class collected

关于java - MethodHandles.lookup().defineClass保留,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52710966/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com