7

在我们的服务器上,我们开始遇到OutOfMemoryError. 我们使用 Eclipse Memory Analysis 分析了堆转储,发现有许多对象被用来做终结处理(大约是堆的 2/3):

在此处输入图像描述

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

"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 <0x4fe053e8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:133)
        - locked <0x4fe053e8> (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,但没有成功,只是OutOfMemoryErrors 的频率减少了,所以我们首先认为它有帮助。

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

Finalizer问题是:对象未最终确定和线程等待下一个对象的原因可能是什么?我们是否正确分析了堆转储?

感谢所有的答案。

4

2 回答 2

1

We think it was related to the OpenJDK version 1.6.0_30. After upgrading to Oracle JDK 1.7.0_51, the problem disappeared. And it probably appeared after automatic update of openJDK, but we cannot confirm this either. We could not find a relevant bug report.

于 2014-03-18T13:14:02.330 回答
0

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

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

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

于 2016-07-23T19:22:13.387 回答