3

它们都可以用于清理,几乎无法保证,但 PR 需要更多的线束编码。那么,有两种选择,为什么我必须偏爱另一种呢?

Javadoc 9将 finalize 描述为非常有问题,但这并不能使其替代方案自动变得更好,对吧?

javadoc 还描述PhantomReference为“在对象变得无法访问时提供更灵活和更有效的方法来释放资源”,但没有指定原因。好吧,我想这些家伙知道一些秘密,但我想知道 - 这个选择不能更明显吗?

区别

以下是我发现的 finalize (FZ) 和 pantom reference (PR) 之间的所有差异,如果我遗漏了什么,请纠正我。

  1. 可用于清理动作?

    • 两者都是。
  2. 需要一个新线程来维护?

    • PR:是的,您必须定义一个队列观察线程才能尽快进行清理
    • FZ:没有
  3. 需要一个新的类来定义?

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

    • 公关:没有
    • FZ:是的,这很方便
  5. 它在我的个人实践中可靠吗?

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

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

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

    • 公关:是的
    • FZ:不,如果严格来说,直接return那么糟糕吗?
  9. 是否指定了多个实例之间的调用顺序?

    • 公关:没有信息
    • FZ:否 - “在调用以完成不同对象的方法时未指定排序”(java.lang.Object)
  10. 保证调用?

    • PR:没有信息 - 您只能“请求通知对象可达性的变化”(java.lang.ref)
    • FZ:否 - “如果有的话,只有在无限期延迟之后才能在可终结对象上调用 finalize 方法”(java.lang.Object)
  11. 关于时间的任何保证?

    • PR:否-“在垃圾收集器确定引用对象的可达性已更改为与引用类型对应的值之后的一段时间”(java.lang.ref)
    • FZ:否 - “Java 编程语言没有指定多久会调用终结器”(JLS),“如果有的话,只有在无限延迟之后才可能在可终结对象上调用终结方法”(java.lang.目的)
  12. 加工过程中可以this复活吗?

    • PR:不,这还不错
    • FZ:是的,官方支持

链接:

4

1 回答 1

4

大部分问题已在是否应首选 Java 9 Cleaner 而非最终确定中得到解决?已经。由于CleanerAPI 建立在 . PhantomReference,因此其中大部分也适用于PhantomReference直接使用。

简而言之,您应该用or替换finalize()用法。对于非内存资源,您应该更喜欢在使用它们后立即显式关闭,并在可行的情况下使用try-with-resources构造。与垃圾收集器的交互可以作为检测编程错误的后备,但不应成为资源清理的首选方式。PhantomReferenceCleaner

在这方面,在资源已正确关闭时选择退出清理的能力具有重要意义,因为这将成为常态。你低估了它的影响。曾经,您的类有一个重要的终结器,它的对象需要两个垃圾回收周期才能被回收,即使finalize()在检查条件后立即返回也是如此。这可以使在次要 gc 中收集或提升到老年代之间有所不同。

最极端的例子是由纯本地对象表示的短期使用资源,在应用逃逸分析后,其内存分配可能会被完全忽略,而非平凡finalize()方法的存在总是意味着全局逃逸,这会阻止这种优化(除其他外,像锁消除)。

虽然CleanerAPI 确实启动了一个专用线程,但在使用PhantomReference. 您也可以在使用资源或即将分配新资源时轮询队列。这并不能保证资源的快速释放(gc 触发的清理无论如何都不能保证),但是您要确保在收集的对象不必要地持有资源时分配不会失败。

即使您使用专用线程进行清理,在您的控制下启动线程与在您的控制之外由未指定的 JVM 线程调用终结器之间存在根本区别,其中finalize()另一个库的错误方法可能会阻塞您清理所需的线程。JVM 可以同时调用多个终结器,而您可以决定使用多少线程进行PhantomReference基于清理的线程。

于 2019-08-19T14:38:18.823 回答