解决java内存泄漏问题:终结?

Rom*_*om1 13 java memory-leaks finalizer

我有一个看似泄漏的行为不当的应用程序.在简要的剖析器调查之后,大多数内存(80%)由java.lang.ref.Finalizer实例保存.我怀疑终结器无法运行.

这种情况的常见原因似乎是终结者抛出的异常.但是,类的finalize方法的javadoc Object(例如,参见这里)似乎与自己相矛盾:它说明了

如果finalize方法抛出未捕获的异常,则忽略该异常并终止该对象的终止.

但后来,它也说明了这一点

finalize方法抛出的任何异常都会导致暂停此对象的终结,但会被忽略.

我应该相信什么(即终止或不结束?),您是否有任何关于如何调查此类明显泄漏的提示?

谢谢

Bri*_*128 9

两个引言都说:

异常将导致暂停/终止此对象的完成.

这两个引述也说:

未被捕获的异常被忽略(即未以任何方式记录或处理VM)

这样就回答了问题的前半部分.我对终结器的了解不够,可以为您提供有关追踪内存泄漏的建议.

编辑:我发现这个页面可能有用.它有一些建议,例如在终结器中手动将字段设置为null以允许GC回收它们.

EDIT2:一些更有趣的链接和引号:

来自Java Finalizer的解剖

终结器线程没有在系统上给出最大优先级.如果"Finalizer"线程无法跟上优先级较高的线程导致可终结对象排队的速率,则终结器队列将继续增长并导致Java堆填满.最终Java堆将耗尽并且将抛出java.lang.OutOfMemoryError.

并且

不保证任何具有finalize()方法的对象都是垃圾回收的.

EDIT3:在阅读了更多的Anatomy链接后,似乎在Finalizer线程中抛出异常确实会降低它的速度,几乎和调用Thread.yield()一样多.您似乎是正确的,即使抛出异常,Finalizer线程最终也会将对象标记为GC'd.但是,由于减速很重要,因此在您的情况下,Finalizer线程可能无法跟上对象创建和超出范围的速率.


Phi*_*vey 6

我的第一步是确定这是否是真正的内存泄漏.

在前面的答案中提出的要点都与收集物体的速度有关,而不是与物体是否被收集的问题有关.只有后者才是真正的内存泄漏.

我们的项目遇到了类似的困境,并以"慢动作"模式运行应用程序,以确定我们是否有真正的泄漏.我们通过减慢输入数据流来实现这一目标.

如果在"慢动作"模式下运行时问题消失,则问题可能是前面答案中建议的问题之一,即Finalizer线程无法足够快地处理终结器队列.

如果这是问题所在,听起来你可能需要做一些非平凡的重构,如Bringer128链接的页面所述,例如

现在让我们看看如何编写需要事后清理的类,以便他们的用户不会遇到之前概述的问题.这样做的最好方法是将这些类拆分为两个 - 一个用于保存需要事后清理的数据,另一个用于保存其他所有 - 并且仅在前者上定义一个终结器