关于垃圾收集:“java.lang.OutOfMemoryError:超出GC开销限制”时GC时间过长的持续时间

Duration of Excessive GC Time in “java.lang.OutOfMemoryError: GC overhead limit exceeded”

偶尔,在每两天一次到每两周一次之间,我的应用程序在代码中看似随机的位置崩溃:java.lang.OutOfMemoryError: GC overhead limit exceeded。 如果我谷歌这个错误,我来到这个问题,并引导我到这个阳光文件,它表示:

The parallel collector will throw an OutOfMemoryError if too much time is
being spent in garbage collection: if more than 98% of the total time is
spent in garbage collection and less than 2% of the heap is recovered, an
OutOfMemoryError will be thrown. This feature is designed to prevent
applications from running for an extended period of time while making
little or no progress because the heap is too small. If necessary, this
feature can be disabled by adding the option -XX:-UseGCOverheadLimit to the
command line.

这告诉我,我的应用程序显然占用了垃圾收集总时间的98%,只能恢复2%的堆。

但98%的时间是什么时候? 应用程序运行的整个两周的98%? 最后一毫秒的98%?

我正在尝试确定实际解决此问题的最佳方法,而不仅仅是使用-XX:-UseGCOverheadLimit,但我觉得需要更好地理解我正在解决的问题。


I'm trying to determine a best approach to actually solving this issue rather than just using -XX:-UseGCOverheadLimit but I feel a need to better understand the issue I'm solving.

好吧,你使用了太多的内存 - 从它的声音来看,这可能是因为内存泄漏缓慢。

您可以尝试使用-Xmx增加堆大小,如果这不是内存泄漏,但有迹象表明您的应用实际上需要大量堆并且您当前具有的设置略微低,这将有所帮助。如果是内存泄漏,这只会推迟不可避免的事情。

要调查它是否是内存泄漏,请指示VM使用-XX:+HeapDumpOnOutOfMemoryError开关在OOM上转储堆,然后分析堆转储以查看是否存在比应有的更多对象。 http://blogs.oracle.com/alanb/entry/heap_dumps_are_back_with是一个非常好的起点。

编辑:由于命运会有它,我碰巧在问这个问题的一天后,在批量风格的应用程序中遇到了这个问题。这不是由内存泄漏引起的,并且增加堆大小也没有帮助。我所做的实际上是减少堆大小(从1GB到256MB)以使更快的GC(尽管更频繁)。 YMMV,但它值得一试。

编辑2:小堆没有解决所有问题...下一步是启用G1垃圾收集器,它似乎比CMS做得更好。


But 98% of what time? 98% of the entire two weeks the application has been running? 98% of the last millisecond?

简单的答案是它没有指定。然而,在实践中启发式"有效",所以它不能是你提出的两种极端解释中的任何一种。

如果您真的想知道测量的间隔是什么,您可以随时阅读OpenJDK 6或7源代码。但我不会打扰,因为它不会帮助你解决问题。

"最佳"方法是对调优进行一些阅读(从Oracle / Sun页面开始),然后仔细"旋转调整旋钮"。它不是很科学,但考虑到目前可用的工具,问题空间(准确预测应用程序+ GC性能)"太难"了。


> 98%将在同一时间段内测量,其中少于2%的内存被恢复。

很可能没有固定的时间段。例如,如果在每1,000,000个对象实时检查之后进行OOM检查。花费的时间取决于机器。

您很可能无法通过添加-XX:-UseGCOverheadLimit来"解决"您的问题。最可能的结果是您的应用程序将慢慢爬行,使用更多的内存,然后达到GC根本不再恢复任何内存的程度。相反,修复内存泄漏,然后(如果仍然需要)增加堆大小。