一些长时间GC停顿问题的排查及解决办法( 二 )


3. 堆空间不够如果应用程序需要的内存比我们执行的Xmx还要大 , 也会导致频繁的垃圾回收 , 甚至OOM 。由于堆空间不足 , 对象分配失败 , JVM就需要调用GC尝试回收已经分配的空间 , 但是GC并不能释放更多的空间 , 从而又回导致GC , 进入恶性循环 。
应用运行时 , 频繁的FullGC会引起长时间停顿 , 在下面这个例子中 , Perm空间几乎是满的 , 并且在Perm区尝试分配内存也都失败了 , 从而触发FullGC:

一些长时间GC停顿问题的排查及解决办法

文章插图
 
同样的 , 如果在老年代的空间不够的话 , 也会导致频繁FullGC , 这类问题比较好办 , 给足老年代和永久代 , 不要做太抠门的人了 , 嘿嘿 。
4. JVM Bug什么软件都有BUG , JVM也不例外 。有时候 , GC的长时间停顿就有可能是BUG引起的 。例如 , 下面列举的这些JVM的BUG , 就可能导致Java应用在GC时长时间停顿 。
一些长时间GC停顿问题的排查及解决办法

文章插图
 
如果你的JDK正好是上面这些版本 , 强烈建议升级到更新BUG已经修复的版本 。
5. 显示System.gc调用检查是否有显示的System.gc调用 , 应用中的一些类里 , 或者第三方模块中调用System.gc调用从而触发STW的FullGC , 也可能会引起非常长时间的停顿 。如下GC日志所示 , Full GC后面的(System)表示它是由调用System.GC触发的FullGC , 并且耗时5.75秒:
一些长时间GC停顿问题的排查及解决办法

文章插图
 
如果你使用了RMI , 能观察到固定时间间隔的FullGC , 也是由于RMI的实现调用了System.gc 。这个时间间隔可以通过系统属性配置:
一些长时间GC停顿问题的排查及解决办法

文章插图
 
JDK 1.4.2和5.0的默认值是60000毫秒 , 即1分钟;JDK6以及以后的版本 , 默认值是3600000毫秒 , 即1个小时 。
如果你要关闭通过调用System.gc()触发FullGC , 配置JVM参数 -XX:+DisableExplicitGC即可 。
那么如何定位并解决这类问题问题呢?
  1. 配置JVM参数:-XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps and -XX:+PrintGCApplicationStoppedTime. 如果是CMS , 还需要添加-XX:PrintFLSStatistics=2 , 然后收集GC日志 。因为GC日志能告诉我们GC频率 , 是否长时间停顿等重要信息 。
  2. 使用vmstat, iostat, netstat和mpstat等工具监控系统全方位健康状况 。
  3. 使用GCHisto工具可视化分析GC日志 , 弄明白消耗了很长时间的GC , 以及这些GC的出现是否有一定的规律 。
  4. 尝试从GC日志中能否找出一下JVM堆碎片化的表征 。
  5. 监控指定应用的堆大小是否足够 。
  6. 检查你运行的JVM版本 , 是否有与长时间停顿相关的BUG , 然后升级到修复问题的最新JDK 。

【一些长时间GC停顿问题的排查及解决办法】


推荐阅读