偶尔,在每两天一次到每两周一次之间,我的应用程序会在代码中看似随机的位置崩溃:java.lang.OutOfMemoryError: GC overhead limit exceeded
。如果我谷歌这个错误,我来this SO question,这导致我this piece of sun documentation表示错误:
如果时间太长,并行收集器将抛出OutOfMemoryError 被用于垃圾收集:如果超过总时间的98% 花在垃圾收集中并且不到2%的堆被恢复了 将抛出OutOfMemoryError。此功能旨在防止 应用程序在制作过程中长时间运行 由于堆太小,很少或根本没有进展。如有必要,这个 可以通过将选项-XX:-UseGCOverheadLimit添加到。来禁用该功能 命令行。
这告诉我,我的应用程序显然占用了垃圾收集总时间的98%,只能恢复2%的堆。
但是98%的时间呢?应用程序运行的整个两周的98%?最后一毫秒的98%?我正在尝试确定实际解决此问题的最佳方法,而不仅仅是使用-XX:-UseGCOverheadLimit
,但我觉得有必要更好地理解我正在解决的问题。
答案 0 :(得分:6)
我正在尝试确定实际解决此问题的最佳方法,而不仅仅是使用
-XX:-UseGCOverheadLimit
,但我觉得有必要更好地理解我正在解决的问题。
嗯,你正在使用太多的内存 - 从它的声音来看,这可能是因为内存泄漏缓慢。
您可以尝试使用-Xmx
增加堆大小,如果这不是内存泄漏,但有迹象表明您的应用实际上需要大量堆并且您当前拥有的设置略微低。如果是内存泄漏,这只会推迟不可避免的事情。
要调查它是否是内存泄漏,请指示VM使用-XX:+HeapDumpOnOutOfMemoryError
开关在OOM上转储堆,然后分析堆转储以查看是否存在比应有的更多对象。 http://blogs.oracle.com/alanb/entry/heap_dumps_are_back_with是一个非常好的起点。
编辑:由于命运会有这种情况,我在问题提出后的一天内,在批量风格的应用中遇到了这个问题。这不是由内存泄漏引起的,并且增加堆大小也没有帮助。我所做的实际上是减少堆大小(从1GB到256MB)以使更快的GC(尽管更频繁)。 YMMV,但值得一试。
编辑2:并非所有问题都由较小的堆解决了...下一步是启用G1 garbage collector,这似乎比CMS做得更好。
答案 1 :(得分:1)
> 98%将在同一时间内测量,其中少于2%的内存被恢复。
很可能没有固定的时间段。例如,如果在每1,000,000个对象实时检查之后进行OOM检查。所需的时间取决于机器。
您很可能无法通过添加-XX:-UseGCOverheadLimit
来“解决”您的问题。最可能的结果是您的应用程序将慢慢爬行,使用更多内存,然后达到GC无法再恢复任何内存的程度。相反,修复内存泄漏,然后(如果仍然需要)增加堆大小。
答案 2 :(得分:1)
但是98%的时间呢?应用程序运行的整个两周的98%?最后一毫秒的98%?
简单的答案是没有指定。然而,在实践中启发式“有效”,所以它不能是你提出的两种极端解释中的任何一种。
如果确实想要了解测量的间隔时间,您可以随时阅读OpenJDK 6或7源代码。但我不会打扰,因为它不会帮助你解决问题。
“最佳”方法是对调整进行一些阅读(从Oracle / Sun页面开始),然后仔细“旋转调整旋钮”。它不是很科学,但考虑到目前可用的工具,问题空间(准确预测应用程序+ GC性能)“太难”了。