为什么应该优先选择PhantomReference而不是finalize?

3

它们都可以用于清理,几乎没有任何保证,但是PR需要更多的硬编码。所以,有两个选项,为什么我必须偏爱一个而不是另一个呢?

Javadoc 9 描述finalize非常有问题,但这并不意味着其替代方案自动更好,对吧?

javadoc还描述了PhantomReference提供了“更灵活和有效的方式来释放资源,当一个对象变得不可达时”,但没有指定原因。好吧,我猜这些家伙知道一些秘密,但我想知道-难道不能更明显地做出这个选择吗?

区别

这里是我发现的finalize(FZ)和pantom reference(PR)之间的所有区别,请纠正我如果我错过了什么。

  1. 可以用于清理操作吗?

    • 两者都可以。
  2. 需要新线程来维护吗?

    • PR:是的,您必须定义一个队列观察器线程以尽快进行清理
    • FZ:不需要
  3. 需要定义新类吗?

    • PR:是的,您必须扩展PhantomReference才能有意义地运行
    • FZ:不需要
  4. 清理处理器可以访问引用对象吗?

    • PR:不可以
    • FZ:可以,这很方便
  5. 在我的个人实践中可靠吗?

    • 两者都可以。
  6. 可能导致性能问题、死锁和挂起吗?

    • 两者都可以。取决于您的代码,不是吗?
  7. 清理处理器中的错误是否会导致资源泄漏?

    • 两者都可以。取决于您的代码,不是吗?
  8. 如果不再需要,是否可以取消?

    • PR:可以
    • FZ:严格来说不行,但是立即return会有什么问题吗?
  9. 多个实例之间的调用顺序是否已指定?

    • PR:没有信息
    • FZ:没有 - "在不同对象的finalize方法的调用之间未指定顺序" (java.lang.Object)
  10. 调用是否保证?

    • PR:没有信息 - 您只能“请求被通知对象可达性更改”(java.lang.ref)
    • FZ:没有 - "如果有的话,finalize方法可能仅在不确定的延迟后才会在可终止的对象上调用"(java.lang.Object)
  11. 关于时间安排有任何保证吗?

    • PR:没有 - "垃圾收集器确定引用物的可达性已更改为与引用类型相对应的值之后的一段时间"(java.lang.ref)
    • FZ:没有 - "Java编程语言不指定finalizer将在多快时被调用"(JLS),"如果有的话,finalize方法可能仅在不确定的延迟后才会在可终止的对象上调用"(java.lang.Object)
  12. 处理期间是否可以使this复活?

    • PR:不可以,这并不是坏事
    • FZ:可以,官方支持

链接:


请参考此答案,了解为什么 finalization 是一个糟糕的机制并已被弃用:https://dev59.com/h1MI5IYBdhLWcg3weLVS#56454348 - Stuart Marks
1个回答

4
大部分内容已经在Java 9清理器是否优于finalization?中有所涉及。由于Cleaner API是建立在PhantomReference之上的,因此大部分内容同样适用于直接使用PhantomReference
简而言之,您不应该用PhantomReferenceCleaner替换finalize()的用法。对于非内存资源,您应该优先使用显式关闭,在使用后立即关闭它们,并在可能的情况下使用try-with-resources结构。与垃圾收集器的交互可能作为检测编程错误的后备,但不应成为资源清理的首选方式。
在这方面,当资源已经正确关闭时选择退出清理的能力具有重要意义,因为这将成为常态。您低估了它的影响。一旦您的类具有非平凡的finalizer,其对象需要两个垃圾回收周期才能被回收,即使finalize()在检查条件后立即返回。这可能导致在minor gc中被收集或晋升到老年代之间的差异。
最极端的例子是一个短暂使用的资源,由纯本地对象表示,其内存分配在应用逃逸分析后可以完全省略,而非平凡的finalize()方法的存在无论如何都意味着全局逃逸,从而阻止了这种优化(以及锁消除等其他优化)。
虽然Cleaner API确实启动了一个专用线程,但在使用PhantomReference时并没有这个要求。您也可以在使用资源时或即将分配新资源时直接轮询队列。这不能保证快速释放资源(gc触发的清理也不能保证),但可以确保在收集对象不必要地持有资源时不会失败。
即使您为清理使用专用线程,但在您控制下启动线程和由不受控制的JVM线程调用终结器之间存在根本差异,在这种情况下,另一个库的错误finalize()方法可能会阻塞所需的清理线程。 JVM可能同时调用多个终结器,而您可以决定使用多少线程来进行基于PhantomReference的清理。

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