gpt4 book ai didi

java - 合成 lambda 类的神奇类卸载?

转载 作者:行者123 更新时间:2023-11-29 04:52:25 26 4
gpt4 key购买 nike

我们通常知道一个类cannot be unloaded来自 ClassLoader,但 lambda 的合成类似乎可以。

证明:

public class Play {
static String test = new String("test");

public static void main(String[] args) throws Exception {
WeakReference<String> wr = new WeakReference<>(test);
Runnable run = test::toString;
System.out.println(Play.class.getClassLoader() == run.getClass().getClassLoader());
System.out.printf("sys=%s, lambda=%s", Runnable.class, run.getClass());
run.run();
test = null;
run = null;
while (wr.get() != null) {
System.gc();
}
}
}

输出:

true
sys=interface java.lang.Runnable, lambda=class Play$$Lambda$1/918221580
<then terminates>

这表明 lambda 的闭包引用的字符串(将被合成类引用)被取消引用,这意味着合成类也必须被 GC 处理。这是有道理的,否则会出现各种基于类加载器的内存泄漏。

我的问题是他们是否打破了无法卸载类的设计来做到这一点?如果是这样,我们可以用我们自己的类来做到这一点吗?

最佳答案

有趣的是,尽管您的测试不足以确定这一点,但实际上,为 lambda 表达式生成的类可以被垃圾收集,如下面的代码可以证明:

import java.lang.invoke.*;
import java.lang.ref.WeakReference;

public class LambdaClassUnloading {
public static void main(String[] args) throws Throwable {
MethodHandles.Lookup l = MethodHandles.lookup();
MethodType t=MethodType.methodType(void.class);
Runnable r=(Runnable)LambdaMetafactory.metafactory(l, "run",
MethodType.methodType(Runnable.class), t,
l.findStatic(LambdaClassUnloading.class, "testMethod", t), t)
.getTarget().invokeExact();
System.out.println("generated "+r);
r.run();
WeakReference<Class<?>> ref=new WeakReference<>(r.getClass());
r=null;
while(ref.get()!=null) System.gc();
System.out.println("class collected");
}
private static void testMethod() {
System.out.println("testMethod() called");
}
}

这会打印类似的东西:

generated java8test.LambdaClassUnloading$$Lambda$1/723074861@77459877
testMethod() called
class collected

虽然数字可能会有所不同。

但出于实际目的,重要的是要了解上面代码反射调用的生成器设施通常是通过 invokedynamic 字节码指令触发的,该指令将永久链接到首先完成引导过程。

换句话说,考虑到当前 JRE 的处理方式,只要包含 invokedynamic 指令的代码是可访问的,永久链接的 lambda 类也会持续存在。这与 Andremoniy’s answer 中演示的行为相匹配; Runnable run = test::toString; 行的代码可以再次执行并将生成与第一次执行相同的类的实例,因为它的构造函数已永久链接到它。

但理论上,替代的 JRE 实现可以让 Bootstrap 方法返回一个 MutableCallSite 并在以后重新定位它,例如生成更优化类的实例或强制在语义等价的 lambda 表达式之间共享类。在那种情况下,当没有更多的 lambda 实例时,现有的 lambda 类可以比它们的定义类更早地被垃圾回收。

所以答案是,确实有一个“神奇的类卸载”,但目前,它与大多数实际用途无关,当然,如果它发生在未来的 JVM 中,它仍然会保留由Java 语言规范。无论您的方法引用 test::toString 捕获的实例如何存储,如果函数接口(interface)的实例 无法访问,它都不会阻碍其收集。


如果您对更多细节感兴趣,文章“anonymous classes in the VM” by John Rose提供介绍。您还可以搜索 sun.misc.Unsafe.defineAnonymousClass...

关于java - 合成 lambda 类的神奇类卸载?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34927171/

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