引用范围过期后的对象垃圾回收
首先让我说我知道 System.gc() 更像是收集垃圾的建议而不是命令,并且不能保证收集完成、启动或收集特定对象。但是...
使用以下代码,仅当我在引用中显式放置 null 时,才会收集 a1 实例。如果 a1 只是超出范围,则不会被收集。
class A {
@Override
protected void finalize() throws Throwable {
System.out.println("Collecting A");
}
}
public class Example {
public static void main(String[] args) throws InterruptedException {
try {
A a1 = new A();
//a1 = null; // a1 gets collected only if I uncomment this line
}
finally {
System.out.println("Finally executed");
}
System.gc();
Thread.sleep(1000);
}
}
为什么 a1 在没有强引用的情况下只是超出范围而不会被收集?是不是因为这个作用域不构成单独的栈帧?
回答
完全由 JVM 决定何时a1收集或是否运行终结器。
例如,JVM 可能决定在解释器中运行它,该解释器只有一个局部变量表,其中包含方法中的每个局部变量 - 类似于字节码中的工作方式 - 并且变量并没有真正“超出范围” ' 从 GC 的角度来看,直到方法退出。
但是,例如,JIT 编译器可能会更积极地限制局部变量的 GC 范围(或者更确切地说,它只是不跟踪不需要的内容)。因此,例如,如果您使用-Xcomp(在 HotSpot JVM 上)运行强制此代码进行 JIT 编译的程序,您可能(或可能不会)看到输出:
Finally executed
Collecting A
没有明确设置a1为null。
另一方面,如果您要使用 VM 选项通过 Epsilon GC(这是一个无操作 GC)运行它:(-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC再次在 HotSpot 上),您可能根本看不到Collecting A输出,即使您明确设置a1为null.
所以,是的,这真的取决于虚拟机。
- In principle, it’s also possible to see the output `Collecting A ␍ Finally executed`