我们能关闭finalizers吗?

16

由于无法保证何时以及是否运行终结器,并且现在终结器几乎被认为是一种不良实践 - 是否有任何方法说服JVM完全跳过所有终结器处理过程?

我之所以提出这个问题,是因为我们有一个庞大的应用程序,当将其移动到更新的JVM(目前不确定是哪个版本)时,会因看起来非常像终结器已知问题(抛出异常,因此GC变得非常缓慢)而瘫痪。

添加

有关排除Java内存泄漏:终结器?的讨论中,建议主要问题是在终结器中抛出异常,因为那会显着减慢终结器处理过程。

当内存较低且堆转储分析显示大量Finalizer对象(超过10,000,000)时,我的问题表现为显着减速 - 这使我认为可能是它们的原因导致了减速,因为它们正在延迟GC。 当然,我可能是错误的。

我没有权力要求进行重构。


3
据我所知,除非进行字节码操作,否则无法做到。如果您认为终结器是问题所在,建议尝试重构应用程序以删除它们。 - CodeChimp
你认为应用程序在最终器中的代码不运行的情况下能正常工作吗?如果我没记错,VM 的正常操作期间将对每个对象进行垃圾回收并运行最终器;唯一的例外是 VM 突然关闭时。 - JimmyB
@HannoBinder 最终器也会使逃逸分析变得不可能。因此,如果应用程序负载过重,它们可能是原因。 - Mikhail
@HannoBinder - 是的,我是这么做的 - 主要是因为Java不能保证何时甚至是否运行终结器,所以代码应该能够幸存下来。 - OldCurmudgeon
1
当然,如果不运行终结器,应用程序逻辑不会受到影响。我更在考虑资源撤消,如果永远不执行,可能通过资源耗尽破坏应用程序。毕竟,那个终结器代码是为了某个原因而编写的。如果可以完全省略它,导致其实现的假设是错误的;但如果它做了一些有用的事情,可能只是在“错误”的地方,关闭终结器代码将会破坏一些东西。 - JimmyB
显示剩余3条评论
4个回答

6
有没有办法说服JVM完全跳过所有终结过程?
答案是否定的。
但是,除非您的对象中有大量带有finalize方法或finalize方法特别昂贵,否则我认为它们不太可能使GC“非常缓慢”。我预计问题出在其他地方。
我建议您打开GC记录以尝试更好地了解实际情况。
但我也同意,长期来看,重构代码以摆脱finalize()方法可能是一件好事。(使用finalize的情况非常少。)
更新-您的新证据相当令人信服,尽管不是证明!
那么,我建议您把证据放在那些有权力要求重构的人面前 :-)。
或者,您可以向涉嫌的finalize方法添加异常处理程序,以查看它们是否会引发异常。 (如果是这样,则更改它们以避免抛出异常...)
但归根结底,如果终结是您性能问题的真正原因,则治愈它们的最佳(甚至可能是唯一)方法是更改代码。

2

可以抑制某些对象的终结。不需要像一位评论者所说的那样进行字节码操作。

该过程在这里有描述,并提供了源代码。类java.lang.ref.Finalizer负责维护尚未完成终结的对象列表。为了抑制您感兴趣的对象的终结,只需使用反射API获取对字段lock的锁定,在Finalizer类维护的链接列表unfinalized上进行迭代,并从此列表中删除您的对象。

我已经尝试过这种方法,可以安全地实例化自定义序列化对象,同时绕过构造函数调用,并避免在正常GC时调用终结器时出现问题。在应用此方法之前,我的JVM会在终结器线程中出现严重故障;自应用此方法以来,故障不再发生。


1

Java 18(于2022年发布)通过JEP 421弃用了finalization,并添加了一个禁用选项

现在,您可以使用--finalization=disabled启动应用程序,它将不会运行finalizers。

java --finalization=disabled -jar your-app.jar

JEP 还提到了一个名为 jdk.FinalizerStatistics 的 JFR 事件,它可以为您提供有关 finalization 的更多信息:
# start Java with the JFR event jdk.FinalizerStatistics enabled
java -XX:StartFlightRecording:filename=recording.jfr -jar your-app.jar
# print events
jfr print --events FinalizerStatistics recording.jfr

0

在Java中,您无法关闭finalizer,但您可以编写代码以便于垃圾回收 :-)


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