我正在阅读 Java平台性能 (很遗憾,自从我最初提出这个问题以来,链接似乎已经从互联网上消失了),而A.3.3节使我感到担忧。
我一直在假设一个超出范围的变量将不再被视为GC的根,但是本文似乎与此矛盾。
最近的JVM(尤其是Sun的1.6.0_07版本)是否仍然有此限制?如果是这样,那么我有很多代码需要分析…
我问这个问题是因为该论文来自1999年-有时情况会发生变化,尤其是在GC领域。
由于该论文不再可用,我想换个说法。该文件暗示,在方法退出之前(而不是在代码块结束之前),在方法内部定义的变量将被视为GC根。因此,必须将变量设置为null,以允许对引用的对象进行垃圾收集。
这意味着在main()方法(或包含无限循环的类似方法)的条件块中定义的局部变量将导致一次性内存泄漏,除非您在变量超出范围之前将其清空。
所选答案中的代码很好地说明了这个问题。在文档中引用的JVM版本上,当foo对象在try块末尾超出范围时,不能对其进行垃圾回收。取而代之的是,JVM会一直保持打开引用直到main()方法的结尾,即使任何人都不可能使用该引用。
这似乎是这样的想法的起源,即即使变量将要超出范围,将变量引用为空也将有助于垃圾回收器。
此代码应清除它:
public class TestInvisibleObject{ public static class PrintWhenFinalized{ private String s; public PrintWhenFinalized(String s){ System.out.println("Constructing from "+s); this.s = s; } protected void finalize() throws Throwable { System.out.println("Finalizing from "+s); } } public static void main(String[] args) { try { PrintWhenFinalized foo = new PrintWhenFinalized("main"); } catch (Exception e) { // whatever } while (true) { // Provoke garbage-collection by allocating lots of memory byte[] o = new byte[1024]; } } }
在我的机器(jdk1.6.0_05)上打印:
从主体构造 从主要完成
从主体构造
从主要完成
因此看来问题已得到解决。
请注意,使用System.gc()代替循环不会由于某种原因而导致对象被收集。