由于几乎不能保证终结器何时运行,即使终结器现在几乎被认为是一种气味,有什么方法可以说服JVM完全跳过所有的终结过程吗?
我之所以这么问,是因为我们有一个庞大的应用程序,当它被移动到一个新的JVM(现阶段不确定是哪个)时,它会因为与finalizer的已知问题(抛出异常,因此GC非常慢)而屈服。
添加
有一些关于java内存泄漏故障排除的讨论:终结?有人建议,当在终结器中抛出异常时,主要问题会出现,因为这会大大减慢终结过程。
当内存变低,堆转储分析显示大量Finalizer
对象(超过10000000个)时,我的问题显示为速度急剧下降,这表明速度下降可能是他们的错,因为他们正在延迟GC。显然我可能错了。
我没有能力要求进行重构。
有什么方法可以说服JVM完全跳过所有的完成过程吗?
在一个单词编号中
但是,除非您的对象中有很大一部分具有finalize
方法和/或finalize
方法特别昂贵,否则我认为它们不太可能使GC"非常慢"。我想问题出在其他方面。
我建议您打开GC日志记录,以便更好地了解实际发生的情况。
但我也同意,从长远来看,重构代码以摆脱finalize()
方法可能是一件好事。(在极少数情况下,使用finalize
确实是最佳解决方案。)
更新-你的新证据很有说服力,尽管不是证据!。
我没有能力要求重构。
那么,我建议你把证据放在那些这样做的人的脚下:-)。
或者,您可以向可疑的finalize
方法添加一个异常处理程序,以查看它们是否正在抛出异常。(如果是,则更改它们以避免抛出异常…)
但最重要的是,如果最终确定是导致性能问题的真正原因,那么解决这些问题的最佳(可能也是唯一)方法就是更改代码。
可以抑制某些对象的终结。正如一位评论者所指出的,它不需要字节码操作。
这里描述了该过程,并提供了源代码。类java.lang.ref.Finalizer
负责维护尚未最终确定的对象的列表。要抑制感兴趣对象的终结,只需使用反射API获取字段lock
上的锁,迭代Finalizer类维护的链表unfinalized
,并从该列表中删除对象即可。
我已经尝试过这种方法来安全地实例化自定义序列化对象,同时绕过构造函数调用,避免使用普通GC调用终结器时出现问题。在应用这个方法之前,我的JVM会在终结器线程中出现硬故障;由于采用了这种方法,故障就不会发生。
Java 18(2022年发布)不赞成使用JEP 421删除最终版本,并添加了禁用选项。
现在,您可以使用--finalization=disabled
启动应用程序,它不会运行终结器。
java --finalization=disabled -jar your-app.jar
JEP还提到了一个名为jdk.FinalizerStatistics
的JFR事件,它为您提供了更多关于定稿的信息:
# 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
您不能在java中关闭finalizer
,但您所能做的就是编写有助于GC的代码:-)。