今天面试,面试官给出了如下代码。他们询问 Java 8 和最新版本的 Java 中的输出是什么。
根据我的理解,由于
B
是一个内部类(非静态嵌套类),它持有对A
实例的引用,这可以防止A
被垃圾收集(GC)。这种行为在 Java 8 中是预料之中的。然而,面试官演示了 A
可以在最新版本的 Java (Java 23) 中进行垃圾收集。然后他询问这个变化是在哪个版本的 Java 中发生的,以及为什么 A
现在可以被 GC 了。我相信这种行为要么与内部类的新机制有关(即使a
仍然存在,
b
也会被GC。原则上,b
应该引用a
)或新机制垃圾收集过程。然而我不知道答案。
class A {
class B {
@Override
protected void finalize() throws Throwable {
System.out.println("B destroy");
}
}
@Override
protected void finalize() throws Throwable {
System.out.println("A destroy");
}
}
public class Solution {
public static void main(String[] args) throws InterruptedException {
A a = new A();
A.B b = a.new B();
a = null;
System.gc();
System.out.println("finish gc");
Thread.sleep(1000);
System.out.println("finish sleep");
}
}
Java 8 的输出
finish gc
finish sleep
Java 23 的输出
finish gc
A destroy
finish sleep
通过对Java 23以下各个版本进行实验,我发现这种现象是从Java 18开始的。然后我查阅了Java 18的新特性。
封闭不使用内部类中省略的实例字段 https://www.oracle.com/java/technologies/javase/18-relnote-issues.html#JDK-8271623
在 JDK 18 之前,当 javac 编译内部类时,它总是生成一个名称以 this$ 开头的私有合成字段,以保存对封闭实例的引用,即使内部类不引用其封闭实例和字段未使用。 从 JDK 18 开始,未使用的 this$ 字段被省略;该字段仅为引用其封闭实例的内部类生成。