垃圾回收细节:这个对象是否有资格进行垃圾回收?

7

我猜你希望有一个像这样的程序......

class Test {
    public static void main(String[] args) {
        new Test();
        System.out.println("done");
    }

    protected void finalize() {
        System.out.println("this object is known to never be referenced.");
    }
}

可能会在"done"之前输出"此对象已知永远不会被引用"。(如果我理解有误,请纠正我!)

此外,编译器/JVM很容易检测到"未读取的本地变量"。例如,在下面的程序中,Eclipse注意到"t本地变量从未被读取"。

然而,对于以下程序的(.class版本),JVM是否可以在"done"之前输出"此对象已知永远不会被引用"是非法的吗?

class Test {
    public static void main(String[] args) {
        Test t = new Test();
        System.out.println("done");
    }

    protected void finalize() {
        System.out.println("this object is known to never be referenced.");
    }
}

大多数垃圾收集文档都谈论到可达性。鉴于从未读取变量t,该对象显然不是“可达”的,对吗?
欢迎参考JLS。

1
至少在C#/.net中,即使在第二个程序中,“完成”消息之前对象的终结可能会发生。这是因为Jitter可以自由地优化变量,一旦清楚它不再被访问。 - CodesInChaos
啊哈,有趣。也许Java也是一样的。虽然我还不确定。 - aioobe
好像在Java中也是类似的。 - aioobe
2个回答

5

Java语言规范12.6.1中说:

12.6.1 实现Finalization

每个对象可以用两个属性来描述:它可能是可达的、终结器可达的或不可达的,还可能是未终结的、可终结的或已终结的。可达对象是任何可以从任何活动线程中访问的对象。可以设计优化程序的转换,将被认为是可达的对象数量减少到比那些朴素地被认为是可达的对象数量更少。

例如,编译器或代码生成器可以选择将不再使用的变量或参数设置为null,以便更早地使该对象的存储器可能被回收。

对我来说,最后一句话恰好涵盖了您所询问的情况。变量t可以在作用域结束之前隐式设置为null,因此使对象不可达。

在C++中,这将是一场灾难,因为很多代码依赖于确切的作用域结束时的销毁时间(例如锁定)。

没错。非常准确!好发现。非常感谢! - aioobe

2
可能会在“完成”之前输出“此对象已知永远不会被引用”的信息。可能会有三种行为:
1. 在“完成”之前输出消息。 2. 在“完成”之后输出消息。 3. 根本不输出“此对象已知永远不会被引用”的消息。
(实际上,最可能的行为是第三种。事实上,除非在创建Test实例和打印“完成”之间生成了大量垃圾,否则几乎可以确定。)
然而,根据下面程序的(.class版本),JVM在“完成”之前输出“此对象已知永远不会被引用”的信息是否合法?
答案是肯定的。Test实例无法从任何活动线程中进行任何潜在的持续计算,因此它是不可访问的。因此,JVM可以立即对其进行垃圾回收和终结处理,这是合法的。
但是,要出现该消息必须发生三件事情:
1. GC必须运行, 2. GC必须将对象检测为不可访问,以及 3. GC必须终结对象。
不能保证所有这些事件都会在应用程序退出之前发生,更不用说在创建实例和打印“完成”之间的时间窗口内发生了。
总之,在Java应用程序中,您永远不应该依赖于终结处理的发生。

是的,谢谢。我知道“没有任何输出”的情况。这就是我测试时发生的情况 :-) 这就是为什么我把它表述为“JVM是否会非法...” - aioobe
@aioobe - 我相信已经回答了。 - Stephen C
啊,没错。有了JLS的引用,实际上就很清楚了!再次感谢。 - aioobe
好久没用Java了...但是当时似乎GC从来没有找到过对象,因为所有程序都只是把计算机放在垃圾桶里 :-D - 6502
@6502 - 那是哪个版本的Java?如果你在说Java 1.0,那已经在1997年被淘汰了。 - Stephen C
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接