为什么 finalize() 只被垃圾收集器调用一次?

Why is finalize() only called once by garbage collector?

SCJP 6 学习指南引述:

In the finalize() method you could write code that passes a reference to the object in question back to another object, effectively uneligiblizing the object for garbage collection. If at some point later on this same object becomes eligible for garbage collection again, the garbage collector can still process this object and delete it. The garbage collector, however, will remember that, for this object, finalize() already ran, and it will not run finalize() again

为什么这样设计? finalize() 方法的目的仍然有效,即使对象第二次被标记为收集。那为什么 Java 决定跳过对 finalize() 的调用?

我不知道这是否是最初的原因,但当前的实现使 Finalizer 个实例排队(Reference) for objects overriding the finalize method with an internal ReferenceQueue 的内部子类,由专用的 FinalizerThread 轮询。

并且因为 JVM 无法知道对象是否需要第二次完成,所以它无法决定在 finalize() 方法完成后是否必须将新的 Finalizer 入队被调用。

无论如何,你应该避免使用finalize()。它使对象分配成本更高,防止逃逸分析,并且不是管理本机资源的非常可靠的方法,因为 GC 可以无限期地推迟完成。

启用终结器的对象不符合收集条件;然而,GC 仅在确定所有其他不符合收集条件的对象后才检查它们,并记下所有 本应 符合收集条件 的对象 已启用的终结器 和 运行 的存在 finalize 此类对象的方法尽快可行。在终结器具有 运行 之前,可终结对象将不符合收集条件,但是 GC 将无法区分在终结器完成后立即符合终结条件的对象,或者那些不符合终结条件的对象某个对象的终结器的操作结果,并在稍后的某个时间符合收集条件。

.NET Framework 包括称为 IIRC GC.SuppressFinalizeGC.ReRegisterForFinalization 的方法,这使得知道对象的终结器不会做任何有用的代码来告诉 GC 不要费心调用它成为可能,并允许知道终结器 运行 "too soon" 的代码稍后再次请求它 运行。然而,JVM 不包含这样的功能。由于一旦终结器 运行s 将所有可终结对象自动重新注册以进行终结将阻止它们被收集,并且由于无法手动重新注册它们,因此最终结果是没有可用的模式来使对象的终结器可以 运行 不止一次。

另一方面,可以通过定义可终结的嵌套 class 对象来实现类似的效果,让外部 class 对象持有对嵌套-class 实例,并将嵌套的 class 实例的 "finalize" 方法链回其所有者中的清理代码。如果该清理代码丢弃嵌套的 class 实例并将其替换为新实例,则该新实例将在发现所有者未被引用的下一个 GC 周期触发其终结器(链接回其所有者) .