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

时间:2011-10-04 07:29:28

标签: java memory-leaks finalizer

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

这种情况的常见原因似乎是终结者抛出的异常。但是,finalize类的Object方法的javadoc(例如参见here)似乎与自身相矛盾:它陈述

  

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

但后来,它也说明了

  

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

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

由于

5 个答案:

答案 0 :(得分:9)

两个引言都说:

  

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

这两个引言也说:

  

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

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

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

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

来自Anatomy of a Java Finalizer

  

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

以及

  

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

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

答案 1 :(得分:6)

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

之前答案中提出的要点都与收集对象的速度有关,而与完全是否收集对象的问题无关。只有后者才是真正的内存泄漏。

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

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

如果这是问题所在,听起来你可能需要进行一些非平凡的重构,如page Bringer128所述,例如,

  

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

答案 2 :(得分:2)

有效Java second edition的第7项是:“避免终结者”。我强烈建议你阅读它。以下是可以帮助您的摘录:

“显式终止方法通常与try-finally构造结合使用以确保终止”

答案 3 :(得分:0)

我和你有同样的问题(下图)。对于我们的情况,因为一个对象在其finalize中有wait(0)并且它永远不会得到通知,这会阻止java.lang.ref.Finalizer $ FinalizerThread。更多参考

objects retained by Finalizer

答案 4 :(得分:0)

我曾经看到过类似的问题,那就是终结者线程无法赶上生成可终结对象的速度。

我的解决方案是通过使用MemoryMXBean .getObjectPendingFinalizationCount(),PD(比例和差异)控制算法来制作闭环控件,以控制我们生成可终结对象的速度,因为我们有一个条目来创建它,只是用pd算法的结果睡觉秒数。它运作良好,但你需要调整pd算法的参数。

希望它有所帮助。