如何在Java中将一个对象标记为finalize(这样就不会再次调用finalize方法)?


问题内容

主要问题在主题中,但是让我展示一下我对Java终结处理的看法,以便我可以再问您一点。

gc通过标记所有活动对象开始垃圾收集。当所有可到达的对象都标记为“活动”时。所有其他对象均不可访问。下一步是检查每个无法到达的对象,并确定是否可以立即将其清除或应首先完成。
如果对象的finalize方法具有主体,则gc会考虑采用另一种方法,然后将此对象终结并应终结。
如果对象的finalize方法的主体为空(受保护的void finalize(){}),则该对象不可终结,可以立即被gc清除。
(我对吗?)
所有可终结对象都将放在同一队列中,以便以后一一终结。据我了解,可终结对象可能会花费大量时间放置在队列中,而等待轮回完成。之所以会发生这种情况,是因为通常只有一个名为Finalizer的线程正在从队列中获取对象并调用其finalize方法,并且当我们在某些对象的finalize方法中进行一些耗时的操作时,队列中的其他对象将等待很长时间才能被完成。好了,当对象完成后,将其标记为“已完成”并从队列中删除。

在下一个垃圾回收过程中,收集器将再次看到此对象不可访问,并且再次具有非空的finalize方法,因此应将该对象放入队列(再次)-但这不是因为收集器以某种方式看到该对象被标记为已完成。

(这是我的主要问题:以何种方式将此对象标记为“已定稿”,收集器如何知道该对象不应再次完成?)


问题答案:

只要我们谈论的是HotSpot JVM …

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

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

一旦使用强引用证明您的对象不可访问,便会处理对该对象的特殊引用。您的对象的FinalizerRef将被添加到终结器队列(链接列表,与其他引用类型相同)。

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

一旦FinalizerRef无效,对象将无法再进入终结器队列。

BTW

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