6

主要问题在主题中,但让我展示一下我对 Java 完成过程的看法,以便我可以再问你一点。

那么 gc 通过标记所有活动对象开始垃圾收集。当所有可达对象都被标记为“活动”时。所有其他对象都无法访问。下一步是检查每个无法到达的对象,并确定它是否可以立即被清扫,或者应该首先完成。如果对象的 finalize 方法有一个主体,那么 gc 会考虑下一个方法,那么这个对象是可终结的并且应该被终结;如果对象的 finalize 方法有一个空主体(受保护的 void finalize(){ }),那么它是不可终结的,现在可以被 gc 清除。(我说得对吗?)
所有可终结的对象都将放在同一个队列中,以便稍后一一终结。据我了解,一个可终结的对象在等待轮到它最终确定时可能会花费大量时间被放入队列中。这可能会发生,因为通常只有一个名为 Finalizer 的线程从队列中获取对象并调用它们的 finalize 方法,并且当我们在某个对象的 finalize 方法中进行一些耗时的操作时,队列中的其他对象将等待很长时间才能完成。好吧,当一个对象已经完成时,它会被标记为 FINALIZED 并从队列中删除。在下一个垃圾收集过程中,收集器将看到该对象(再次)无法访问并且具有非空的 finalize 方法(再次)所以该对象应该被放入队列中(再次) - 但它不会因为收集器以某种方式看到这个对象被标记为 FINALIZED。(这是我的主要问题:这个对象以什么方式被标记为 FINALIZED,收集器如何知道这个对象不应该再次被最终确定?)

4

3 回答 3

5

只要我们在谈论 HotSpot JVM ...

对象本身未标记为已完成。

每次创建新的 finalize 对象时,JVM 都会创建一个额外的对象 FinalizerRef(这有点类似于 Weak/Soft/Phantom 引用)。

一旦通过强引用证明您的对象无法访问,就会处理对该对象的特殊引用。您对象的 FinalizerRef 将被添加到终结器队列(这是链表,与其他引用类型相同)。

当终结器线程从队列中使用 FinalizerRef 时,它会将其指向对象的空指针设为空(尽管线程将保持对对象的强引用,直到终结器完成)。

一旦 FinalizerRef 无效,对象就不能再进入终结器队列。

顺便提一句

-XX:+PrintReferenceGC您可以使用(查看更多 GC 诊断 JVM 选项)在 GC 日志中查看首选项处理时间(和引用数量)

于 2014-02-17T12:32:42.457 回答
1

JVM 将元数据存储在对象头中。任何具有子类 finalize() 的对象都会被调用,即使是空的。入队不需要很长时间,但可以在队列中等待很长时间。

于 2014-02-16T11:31:20.970 回答
1

我不知道真正的、已实现的最终确定过程究竟是如何工作的,但如果我必须这样做,我会这样做——在对象元数据中存储一个三态标志,告诉 GC 对象是否刚刚停止存在在使用中,需要运行终结器,或者可能被移除。您可能必须检查 java 源代码以获取详细信息,但这应该是整体模式:

(在新)

object.metadata.is_finalized=NEEDS_FINALIZE;

(在 gc 中)

while ((object=findUnreachableObject())!=null) {
    if (object.metadata.is_finalized==NEEDS_FINALIZE) {
        if (hasNonNullBody(object.finalize)) {
            Finalizer.addForProcessing(object);
            object.metadata.is_finalized=IN_FINALIZER_QUEUE;
        } else {
            object.metadata.is_finalized=REMOVE_NOW;
        }
    }
    if (object.metadata.is_finalized==REMOVE_NOW) {
        // destroy the object and free the memory
    }
}

(在终结器中)

while ((object=getObjectForProcessing)!=null) {
    object.finalize();
    object.metadata.is_finalized=REMOVE_NOW;
}
于 2014-02-16T14:36:49.143 回答