由于内存泄漏或其他内存问题,经常导致应用程序冻结,仅使垃圾收集器(GC)进程运行失败,试图释放一些空间。 直到看门狗(或沮丧的管理员)重新启动应用程序并且问题从未解决之前,这种情况一直发生。
本文的目的是说明如何识别过多的GC,以及如何在发生堆垃圾时进行堆转储。 假定Hotspot JVM 1.6或更高版本。
相关的JVM标志
使用以下标志,我们可以指示Hotspot JVM在应用程序变为GC驱动时引发堆转储。
首先,应添加-XX:+ HeapDumpOnOutOfMemoryError标志。
我们的目标是在由于不停止GC而导致应用程序吞吐量下降时生成OutOfMemory错误。 有两个JVM标志将有帮助:
- -XX: GCTimeLimit = 98 –定义抛出OutOfMemory错误之前在GC中花费的时间比例的限制
- -XX: GCHeapFreeLimit = 2 –定义完整GC之后抛出OutOfMemoryError之前的可用空间的最小百分比
默认情况下,两个标志都是启用的,但是内存不足错误不会经常触发。 因此,需要更深入的了解才能确定每个标志何时有用以及默认值是什么。
Oracle文档
过多的GC时间和OutOfMemoryError。
如果在垃圾回收上花费了太多时间,则并发收集器将抛出OutOfMemoryError:如果在垃圾回收中花费了总时间的98%以上,而回收的堆少于2%,则将抛出OutOfMemoryError。 此功能旨在防止应用程序长时间运行,而由于堆太小而几乎没有进展,甚至没有进展。 如有必要,可以通过在命令行中添加选项-XX:-UseGCOverheadLimit来禁用此功能。
该策略与并行收集器中的策略相同,除了执行并发收集所花费的时间不计入98%的时间限制。 换句话说,只有在应用程序停止时执行的收集才计入过多的GC时间。 此类收集通常是由于并发模式故障或显式收集请求(例如,对System.gc()的调用)引起的。
如第二段所述, GCTimeLimit在并行收集器上不能很好地工作,因为在这种情况下花费在GC上的时间不太重要。
为GCHeapFreeLimit选择默认值
选择默认值的方式应使当应用程序变得无响应(或由GC驱动)时,将导致内存不足。 无响应性是一个主观定义,可能因应用程序而异,因此,对于不同的应用程序,其结果值可能会(略有不同)不同。
首先,应该创建一个简单的测试。 在应用程序内部运行并分配大量对象的类。 可以快速收集部分对象,并可以长期保留部分对象。
在运行测试之前,应添加GC日志记录标志。 对于我的应用程序,添加了以下标志:
-Xmx960m-详细:gc -XX:+ PrintGCDetails -XX:+ PrintGCDateStamps -XX:+ PrintTenuringDistribution -Xloggc:/root/gc.log -XX:+ HeapDumpOnOutOfMemoryError -XX:HeapDumpPath = / root / dumps
GC日志的输出如下所示。 第一个完整的GC周期–应用程序响应Swift:
[完整GC [PSYoungGen:290112K-> 33213K(308672K)] [PSOldGen:655359K-> 655359K(655360K)] 945471K-> 688573K(964032K)]
几分钟后–应用程序无响应:
[完整GC [PSYoungGen:290112K-> 213269K(308672K)] [PSOldGen:655359K-> 655359K(655360K)] 945471K-> 868629K(964032K)]
对于950 Mb堆,内存空间的分配为:老gen = 650Mb,年轻= 300Mb。 当应用程序响应时,老一代将充满强大的参考数据,而大多数年轻一代则是垃圾。
因此,GCHeapFreeLimit的一个估计是(清除所有年轻人,不清除任何旧东西)300/950〜32%。
但是,随着时间的流逝,无法将年轻空间中的物体提升为旧空间,因为空间已满。 就像在Oracle文档中所说的那样: 最年轻的集合并不需要保证完全升级所有活动对象。 (-XX:+ HandlePromotionFailure标志)。
由于促销失败,因此引用的对象比年轻一代期望的要多得多。 为了安全起见(减少误报),我假设当年轻空间的1/3以上是垃圾空间而旧空间已满时,系统由GC驱动。
因此,我建议类似我的应用的比率是200/950〜20%。
-XX: GCHeapFreeLimit = 20
实验表明,OOM在超出20%限制的1-2分钟过量GC后发生,在此期间发生30-35个完整GC。
结论
在没有灵丹妙药的Java中,识别过多的垃圾回收是一项艰巨的任务。 热点JVM开发人员提供了一些方法,这些方法将经常有助于识别根本原因。 如果正确使用GCHeapFreeLimit和GCTimeLimit标志,可以使用适当的值来阐明问题。
参考文献
1. http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
2. http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html
参考:来自The Art of Java博客的JCG合作伙伴 Art Gourevitch 在Hotspot JVM中跟踪过多的垃圾收集 。
翻译自: https://www.javacodegeeks.com/2012/06/tracking-excessive-garbage-collection.html