【问题标题】:Replacing finalize() in Java在 Java 中替换 finalize()
【发布时间】:2017-12-11 23:16:41
【问题描述】:

Object.finalize() 在 Java 9 中已被弃用,我想我理解其中的原因,但我无法找到如何替换它。

我有一个名为 Configuration 的实用程序类,它本质上只有一个实例,它拥有应用程序中的所有内容并在应用程序期间持续存在。它提供的服务之一是记录:在第一次请求记录消息时,会创建一个记录器(由于各种遗留原因,它是我自己的记录器而不是标准记录器),并在配置对象的字段中保存一个引用,并且在应用程序终止时,无论是正常还是异常,我都想释放记录器持有的任何资源(这是一个黑匣子,因为我的库的用户可以提供他们自己的实现)。

目前这是通过调用logger.close()Configuration.finalize() 方法实现的。

我应该怎么做?

【问题讨论】:

  • FYI
  • 由于应用程序终止时没有发生最终确定,因此您的旧方法从未奏效,而您却没有意识到。那么你确定你需要这种清理吗?

标签: java garbage-collection java-9 finalizer


【解决方案1】:

Java 9 引入了 Cleaner 和 Cleanable 实用程序类,它们负责将幻像引用连接到队列和清空该队列的清理线程。

虽然这可以让您分离出将在拥有对象死亡后执行事后清理的见证,但所有关于 GC 触发的资源管理的警告仍然适用,即仍然最好依赖 AutoClosable 和try-with-resources 块来管理资源的生命周期,而不是在垃圾收集器上。

【讨论】:

【解决方案2】:

Phantom referencesfinalize() 的一般替代品。 Java 运行时中的许多类已经在使用它们。

使用Phantom references有点费力,你必须维护自己的引用列表和事后处理线程。另一方面,您完全可以控制。

Here is a simple example 的 Phantom 参考设置。

This article 解释和比较 Java finalize()Phantom references 的实现。

【讨论】:

  • 谢谢。引用的文章是关于我见过的对幻像引用的最易读的解释;阅读后清楚的两件事是(a)人们发现它们很难解释,以及(b)要使它们工作,您必须用大约 6 的精细结构替换单行 finalize() 方法单独的类。 (可以假设如果 github 示例被认为是“简单的”,那么这并不容易。)不鼓励,但谢谢!
  • 嗯,最好的方法是不要将对象生命周期与 GC 结合起来。应将特殊参考(终结器、幻象等)的使用视为最后的手段。不幸的是,有时你必须走这条路。
【解决方案3】:

您可以将线程作为shutdown hook 添加到运行时:

Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    // cleanup code
}));

这在 VM 终止时调用,因此在您的特定情况下它应该是 finalize 的一个很好的替代品

【讨论】:

  • 它适用于许多场景。但是在某些情况下,VM 在运行完我的应用程序后会继续做其他事情。当然,我不想在 VM 完成之前将 Configuration 对象锁定在内存中。
【解决方案4】:

恕我直言,告诉记录器清理自己的烂摊子不是您的应用程序的责任。记录器本身可以并且应该这样做(与 IO 流或 DB 连接不同),它应该存在很长时间。

但是你已经提供了logger.close()...好吧,那么我建议如下:

  • 确保close 是幂等的,即第二次关闭记录器是无操作的。
  • 同时使用Runtime#addShutdownHookPhantomReference,并让它们都调用logger.close(),以便在JVM 终止或您的应用程序被GC'ed 时都调用它。

【讨论】:

  • 是否建议使用 Runtime#addShutdownHook 解决配置对象被锁定在内存中,正如 Michael 对 Lothar 的回答所指出的那样。这怎么可能解决?谢谢。
猜你喜欢
  • 2020-03-20
  • 2017-09-04
  • 2013-11-18
  • 1970-01-01
  • 2012-03-22
  • 1970-01-01
  • 1970-01-01
  • 2011-07-07
  • 2017-11-07
相关资源
最近更新 更多