对象未完成,Finalizer线程没有做任何事情

在我们的服务器上,我们开始遇到OutOfMemoryError问题。 我们使用Eclipse Memory Analysis分析了堆转储,并发现许多对象被认为是最终确定的(大约是堆的2/3):

在此处输入图像描述

我们发现,它可能是一些finalize()方法阻塞。 我发现了这个问题的几个错误报告( 这里或这里 ),它总是在Finalizer线程堆栈中表现出来,它在某个地方被阻止了。 但在我们的例子中,这个post是等待的:

 "Finalizer" daemon prio=10 tid=0x43e1e000 nid=0x3ff in Object.wait() [0x43dfe000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on  (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:133) - locked  (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:149) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:189) 

编辑:

然后我们尝试添加-XX:+UseConcMarkSweepGC ,但没有成功,只有OutOfMemoryError的频率减少了,所以我们首先想到它有所帮助。

最后,我们怀疑JVM错误并从OpenJDK 1.6.0_30升级到Oracle JDK 1.7.0_51,问题消失了(至少看起来如此,在过去4小时内使用的堆没有增长)。 我们不记得finalize方法有任何变化,我们也没有升级任何库,在那段时间里只有很小的发展。 问题不会在我们的测试服务器上重现,具有相同的配置,除了它是64位JVM而生产服务器是32位。

问题是:对象未完成的原因是什么, Finalizer线程等待下一个对象? 我们是否正确分析了堆转储?

谢谢你的所有答案。

我们认为它与OpenJDK版本1.6.0_30有关。 升级到Oracle JDK 1.7.0_51后,问题就消失了。 它可能出现在openJDK的自动更新之后,但我们也无法确认。 我们找不到相关的错误报告。

Finalizer线程具有低优先级,因此将花费大量时间WAITING而不是最终确定。 我不会从该堆栈跟踪中得出线程被阻塞的结论; 它只是放弃了对其他线程的控制。 相反,您可能会在终结器队列中引入病态数量的对象 ,而JVM根本无法跟上。

遗憾的是,有太多可能的解释为什么这种行为在版本之间发生变化以确定确切的原因,但这里有一个可能的解释。 Oracle的Java 7有一个新的,更高效的垃圾收集器 。 可以合理地设想GC上的负载减轻意味着终结器队列在处理器上获得更多时间,因此能够跟上添加到它的对象数量。

但是,无论根本原因是什么,正确的解决方案是减少终结器的使用。 除非在非常有限的情况下,他们引入的问题多于他们解决的问题,其中最重要的是GC和内存开销。 如果您发现自己正在调查正在等待最终化清理的对象,那么您构建的可终结对象太多了。