来自SCJP 6学习指南的引言:
在
finalize()
方法中,您可以编写传递引用的代码 将有问题的对象转回给另一个对象,有效地使对象无法进行垃圾回收。如果稍后在同一个对象上再次符合垃圾回收条件,垃圾收集器仍然可以处理该对象并将其删除。但是,垃圾收集器会记住,对于此对象,finalize()
已经运行,并且它不会再次运行finalize()
为什么设计如此?即使第二次将对象标记为集合,finalize()
方法的目的仍然很好。那么为什么Java决定跳过对finalize()
的调用?
答案 0 :(得分:4)
我不知道它是否是最初的原因,但当前实现将Finalizer
实例(Reference
的内部子类)排入队列,以覆盖使用内部ReferenceQueue
覆盖finalize方法的对象由专门的FinalizerThread
进行查询。
并且因为JVM无法知道对象是否需要第二次完成,所以无法确定在调用Finalizer
方法后是否必须将新的finalize()
排入队列
无论如何,你应该避免使用finalize()
。它使对象分配成本更高,防止转义分析,并且不是一种非常可靠的管理本机资源的方法,因为GC可以在无限的时间内推迟完成。
答案 1 :(得分:2)
具有启用的终结器的对象不符合收集条件;然而,在确定了所有其他不符合收集条件的对象之后,GC才会检查它们,并记下所有本来符合收集资格的对象,但是对于已启用的对象终结器,并尽快运行此类对象的finalize
方法。在终结器运行之前,可终结的对象不会有资格收集,但是GC将无法区分在终结器完成后立即有资格完成的对象,或者因为最终确定而无法完成的对象一些物品的终结者采取的行动,并有资格在以后收集。
.NET Framework包含名为IIRC GC.SuppressFinalize
和GC.ReRegisterForFinalization
的方法,这使得知道对象终结器的代码无法做任何有用的事情来告诉GC打扰它,并允许知道终结器的代码运行太快"要求它稍后再次运行。但是,JVM不包含此类功能。由于在终结器运行后自动重新注册所有可终结对象以进行最终确定,因此无法收集它们,并且由于无法手动重新注册它们,因此最终结果是没有可用的模式通过哪个对象的终结器可以多次运行。
另一方面,可以通过定义一个可终结的嵌套类对象来实现类似的效果,使外部类对象保持对嵌套类实例的引用,并具有该嵌套类实例' s"敲定"方法链返回其所有者的清理代码。如果该清理代码丢弃嵌套类实例并将其替换为新实例,则该新实例将在下一个GC循环中触发其终结器(链接回其所有者),其中发现所有者未被引用。