对于许多企业级应用,尤其是OLTP应用来说,长暂停很可能导致服务超时,而对这些运行在JVM上的应用来说,垃圾回收(GC)可能是长暂停最主要的原因。本文将描述一些可能碰到GC长暂停的不同场景,以及说明我们如何排查和解决这些GC停顿的问题。

下面是一些应用在运行时,可能导致GC长暂停的不同场景。

1. 碎片化

这个绝对要排在第一位。因为,正是因为碎片化问题--CMS最致命的缺陷,导致这个统治了OLAP系统十多年的垃圾回收器直接退出历史舞台(CMS已经是deprecated,未来版本会被移除,请珍惜那些配置了CMS的JVM吧),面对G1以及最新的ZGC,天生残(碎)缺(片)的CMS毫无还手之力。

对于CMS,由于老年代的碎片化问题,在YGC时可能碰到晋升失败(promotion failures,即使老年代还有足够多有效的空间,但是仍然可能导致分配失败,因为没有足够连续的空间),从而触发Concurrent Mode Failure,发生会完全STW的FullGC。FullGC相比CMS这种并发模式的GC需要更长的停顿时间才能完成垃圾回收工作,这绝对是Java应用最大的灾难之一。

为什么CMS场景下会有碎片化问题?由于CMS在老年代回收时,采用的是标记清理(Mark-Sweep)算法,它在垃圾回收时并不会压缩堆,日积月累,导致老年代的碎片化问题会越来越严重,直到发生单线程的Mark-Sweep-Compact GC,即FullGC,会完全STW。如果堆比较大的话,STW的时间可能需要好几秒,甚至十多秒,几十秒都有可能。

接下来的cms gc日志,由于碎片率非常高,从而导致promotion failure,然后发生concurrent mode failure,触发的FullGC总计花了17.1365396秒才完成:

{Heap before GC invocations=7430 (full 24):parnew generation total 134400K, used 121348K[0x53000000, 0x5c600000, 0x5c600000)eden space 115200K, 99% used [0x53000000, 0x5a07e738, 0x5a080000)from space 19200K, 32% used [0x5a080000, 0x5a682cc0, 0x5b340000)to space 19200K, 0% used [0x5b340000, 0x5b340000, 0x5c600000)concurrent mark-sweep generation total 2099200K, used 1694466K [0x5c600000, 0xdc800000, 0xdc800000)concurrent-mark-sweep perm gen total 409600K, used 186942K [0xdc800000, 0xf5800000, 0xfbc00000)10628.167: [GC Before GC:Statistics for BinaryTreeDictionary:------------------------------------Total Free Space: 103224160Max Chunk Size: 5486Number of Blocks: 57345Av. Block Size: 1800Tree Height: 36 <---- High fragmentationStatistics for IndexedFreeLists:--------------------------------Total Free Space: 371324Max Chunk Size: 254Number of Blocks: 8591 <---- High fragmentationAv. Block Size: 43free=103595484frag=1.0000 <---- High fragmentationBefore GC:Statistics for BinaryTreeDictionary:------------------------------------Total Free Space: 0Max Chunk Size: 0Number of Blocks: 0Tree Height: 0Statistics for IndexedFreeLists:--------------------------------Total Free Space: 0Max Chunk Size: 0Number of Blocks: 0free=0 frag=0.000010628.168: [ParNew (promotion failed) Desired survivor size 9830400 bytes, new threshold 1 (max 1)- age 1: 4770440 bytes, 4770440 total: 121348K->122157K(134400K), 0.4263254secs]10628,594: [CMS10630.887: [CMS-concurrent-mark: 7.286/8.682 secs] [Times: user=14.81, sys=0.34, real=8.68 secs](concurrent mode failure):1698044K->625427K(2099200K), 17.1365396 secs]1815815K->625427K(2233600K), [CMS Perm : 186942K->180711K(409600K)]After GC:Statistics for BinaryTreeDictionary:------------------------------------Total Free Space: 377269492Max Chunk Size:377269492Number of Blocks: 1 <---- No fragmentationAv. Block Size: 377269492Tree Height: 1 <---- No fragmentationStatistics for IndexedFreeLists:--------------------------------Total Free Space: 0Max Chunk Size: 0Number of Blocks: 0free=377269492frag=0.0000 <---- No fragmentationAfter GC:Statistics for BinaryTreeDictionary:------------------------------------Total Free Space: 0Max Chunk Size: 0Number of Blocks: 0Tree Height: 0Statistics for IndexedFreeLists:--------------------------------Total Free Space: 0Max Chunk Size: 0Number of Blocks: 0free=0 frag=0.0000, 17.5645589 secs] [Times: user=17.82 sys=0.06, real=17.57 secs]Heap after GC invocations=7431 (full 25):parnew generation total 134400K, used 0K [0x53000000, 0x5c600000, 0x5c600000)eden space 115200K, 0% used [0x53000000, 0x53000000, 0x5a080000)from space 19200K, 0% used [0x5b340000, 0x5b340000, 0x5c600000)to space 19200K, 0% used [0x5a080000, 0x5a080000, 0x5b340000)concurrent mark-sweep generation total 2099200K, used 625427K [0x5c600000, 0xdc800000, 0xdc800000)concurrent-mark-sweep perm gen total 409600K, used 180711K [0xdc800000, 0xf5800000, 0xfbc00000)}Total time for which application threads were stopped: 17.5730653 seconds
复制代码

2. GC时操作系统的活动

当发生GC时,一些操作系统的活动,比如swap,可能导致GC停顿时间更长,这些停顿可能是几秒,甚至几十秒级别。

如果你的系统配置了允许使用swap空间,操作系统可能把JVM进程的非活动内存页移到swap空间,从而释放内存给当前活动进程(可能是操作系统上其他进程,取决于系统调度)。Swapping由于需要访问磁盘,所以相比物理内存,它的速度的令人发指。所以,如果在GC的时候,系统正好需要执行Swapping,那么GC停顿的时间一定会非常非常非常恐怖。

下面是一段持续了29.48秒的YGC日志:

{Heap before GC invocations=132 (full 0):par new generation total 2696384K, used 2696384K [0xfffffffc20010000, 0xfffffffce0010000, 0xfffffffce0010000)eden space 2247040K, 100% used [0xfffffffc20010000, 0xfffffffca9270000, 0xfffffffca9270000)from space 449344K, 100% used [0xfffffffca9270000, 0xfffffffcc4940000, 0xfffffffcc4940000)to space 449344K, 0% used [0xfffffffcc4940000, 0xfffffffcc4940000, 0xfffffffce0010000)concurrent mark-sweep generation total 9437184K, used 1860619K [0xfffffffce0010000, 0xffffffff20010000, 0xffffffff20010000)concurrent-mark-sweep perm gen total 1310720K, used 511451K [0xffffffff20010000, 0xffffffff70010000, 0xffffffff70010000)2013-07-17T03:58:06.601-0700: 51522.120: [GC Before GC: :2696384K->449344K(2696384K), 29.4779282 secs] 4557003K->2326821K(12133568K) ,29.4795222 secs] [Times: user=915.56, sys=6.35, real=29.48 secs]
复制代码

最后一行[Times: user=915.56, sys=6.35, real=29.48 secs]中real就是YGC时应用真实的停顿时间。

发生YGC的这个时间点,vmstat命令输出结果如下:

r b w swap free re mf pi po fr de sr s0 s1 s2 s3 in sy cs us sy id0 0 0 77611960 94847600 55 266 0 0 0 0 0 0 0 0 0 3041 2644 2431 44 8 480 0 0 76968296 94828816 79 324 0 18 18 0 0 0 0 1 0 3009 3642 2519 59 13 281 0 0 77316456 94816000 389 2848 0 7 7 0 0 0 0 2 0 40062 78231 61451 42 6 532 0 0 77577552 94798520 115 591 0 13 13 0 0 13 12 1 0 4991 8104 5413 2 0 98
复制代码
YGC总计花了29秒才完成。vmstat命令输出结果表示,可用swap空间在这个时间段减少了600m。这就意味着,在GC的时候,内存中的一些页被移到了swap空间,这个内存页不一定属于JVM进程,可能是其他操作系统上的其他进程。

从上面可以看出,操作系统上可用物理内容不足以运行系统上所有的进程,解决办法就是尽可能运行更少的进程,增加RAM从而提升系统的物理内存。在这个例子中,Old区有9G,但是只使用了1.8G(mark-sweep generation total 9437184K, used 1860619K)。我们可以适当的降低Old区的大小以及整个堆的大小,从而减少内存压力,最小化系统上的应用发生swapping的可能。

除了swapping以外,我们也需要监控了解长GC暂停时的任何IO或者网络活动情况等, 可以通过iostat和netstat两个工具来实现. 我们还能通过mpstat查看CPU统计信息,从而弄清楚在GC的时候是否有足够的CPU资源。

3. 堆空间不够

如果应用程序需要的内存比我们执行的Xmx还要大,也会导致频繁的垃圾回收,甚至OOM。由于堆空间不足,对象分配失败,JVM就需要调用GC尝试回收已经分配的空间,但是GC并不能释放更多的空间,从而又回导致GC,进入恶性循环。

应用运行时,频繁的FullGC会引起长时间停顿,在下面这个例子中,Perm空间几乎是满的,并且在Perm区尝试分配内存也都失败了,从而触发FullGC:

166687.013: [Full GC [PSYoungGen:126501K->0K(922048K)] [PSOldGen: 2063794K->1598637K(2097152K)]2190295K->1598637K(3019200K) [PSPermGen: 165840K->164249K(166016K)],6.8204928 secs] [Times: user=6.80 sys=0.02, real=6.81 secs]166699.015: [Full GC [PSYoungGen:125518K->0K(922048K)] [PSOldGen: 1763798K->1583621K(2097152K)]1889316K->1583621K(3019200K) [PSPermGen: 165868K->164849K(166016K)],4.8204928 secs] [Times: user=4.80 sys=0.02, real=4.81 secs]
复制代码
同样的,如果在老年代的空间不够的话,也会导致频繁FullGC,这类问题比较好办,给足老年代和永久代,不要做太抠门的人了,嘿嘿。

4. JVM Bug

什么软件都有BUG,JVM也不例外。有时候,GC的长时间停顿就有可能是BUG引起的。例如,下面列举的这些JVM的BUG,就可能导致Java应用在GC时长时间停顿。

6459113: CMS+ParNew: wildly different ParNew pause times depending on heap shape caused by allocation spreadfixed in JDK 6u1 and 76572569: CMS: consistently skewed work distribution indicated in (long) re-mark pausesfixed in JDK 6u4 and 76631166: CMS: better heuristics when combatting fragmentationfixed in JDK 6u21 and 76999988: CMS: Increased fragmentation leading to promotion failure after CR#6631166 got implementedfixed in JDK 6u25 and 76683623: G1: use logarithmic BOT code such as used by other collectorsfixed in JDK 6u14 and 76976350: G1: deal with fragmentation while copying objects during GCfixed in JDK 8
复制代码
如果你的JDK正好是上面这些版本,强烈建议升级到更新BUG已经修复的版本。

5. 显示System.gc调用

检查是否有显示的System.gc调用,应用中的一些类里,或者第三方模块中调用System.gc调用从而触发STW的FullGC,也可能会引起非常长时间的停顿。如下GC日志所示,Full GC后面的(System)表示它是由调用System.GC触发的FullGC,并且耗时5.75秒:

164638.058: [Full GC (System) [PSYoungGen: 22789K->0K(992448K)][PSOldGen: 1645508K->1666990K(2097152K)] 1668298K->1666990K(3089600K)[PSPermGen: 164914K->164914K(166720K)], 5.7499132 secs] [Times: user=5.69, sys=0.06, real=5.75 secs]
复制代码
如果你使用了RMI,能观察到固定时间间隔的FullGC,也是由于RMI的实现调用了System.gc。这个时间间隔可以通过系统属性配置:
-Dsun.rmi.dgc.server.gcInterval=7200000-Dsun.rmi.dgc.client.gcInterval=7200000
复制代码

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。

最后

后续会持续更新JVM专题知识及更多架构专题,大家觉得不错可以点个赞在关注下,以后还会分享更多文章!

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

  1. 如何排查、解决那些长时间GC停顿的问题

    对于许多企业级应用,尤其是OLTP应用来说,长暂停很可能导致服务超时,而对这些运行在JVM上的应用来说,垃圾回收(GC)可能是长暂停最主要的原因.本文将描述一些可能碰到GC长暂停的不同场景,以及说明我 ...

  2. 转帖:长时间坐着工作腰疼的解决办法

    揉:取坐姿,两手五指并拢,分别放在左右后腰椎部,掌心向内,上下缓慢揉搓,至发热为止. 滚:两手握拳,放腰部向四周滚动.按摩,自下而上,自上而下,反复多次进行.头部可配合前倾后仰. 推:两手对搓发热之后 ...

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

    对于许多企业级应用,尤其是OLTP应用来说,长暂停很可能导致服务超时,而对这些运行在JVM上的应用来说,垃圾回收(GC)可能是长暂停最主要的原因.本文将描述一些可能碰到GC长暂停的不同场景,以及说明我 ...

  4. Mysql死锁排查及解决办法

    Mysql死锁排查及解决办法 1. 查看当前被锁的表 show OPEN TABLES where In_use > 0; +----------+-------+--------+------ ...

  5. 小米Pro15安装Ubuntu20.04LTS后Window10的时间提前了8小时 , 的解决办法 2021-03-04 16:50

    小米Pro15安装Ubuntu20.04LTS后Window10的时间提前了8小时 小米Pro15安装Ubuntu20.04LTS后Window10的时间提前了8小时 问题 原因 解决办法 总结 问题 ...

  6. 黑苹果双系统时间不一致_黑苹果与Windows系统时间不对(不同步)的解决办法...

    唠叨 经过我一番蒸腾,终于把系统给上好了,目前还缺个WiFi,等着把网卡淘回来换上就可以完美支持WiFi,隔空投送,以及苹果的各个功能. 先上个电脑配置吧,我的是机械革命深海幽灵z2air-s.这是我 ...

  7. 【Field xx in xxx.xx.xx required a bean of type ‘xx.xx.xx‘ that could not be found】排查和解决办法

    解决springboot启动报以下错误的解决办法 问题现象 解决办法 总结 Field testMapper in com.micro.test.TestServiceImpl required a ...

  8. Eclipse执行java代码,获取系统时间少了8个小时,解决办法

    今天在Eclipse上做一个压缩一个空文件夹目录的程序,测试的时候,发现java打印出来的系统时间new java.util.Date()比我的电脑时间少了8个小时. 经过网上查找资料,发现原来是时区 ...

  9. ADI公司的DSP仿真器常见故障/损坏的排查与解决办法(CCES)

    ADI的DSP,Blackfin和SHARC来讲,有两种开发软件,一个是Visual DSP++,一个是CCES. Visual DSP++是早期的开发软件,我的印象中从2000年开始的1.0吧,经历 ...

最新文章

  1. python机器学习包
  2. c++ string c_str() 和data()区别
  3. 成功使用Windows Live Writer 2010发布日志
  4. Solve The Maze CodeForces - 1365D(贪心+dfs)
  5. 功能项目拼图将Java 9引入
  6. VC++动态链接库编程(转载)
  7. C语言设计农历万年历论文,万年历设计报告
  8. 财务人员必备的5个Excel技能,学会工资高出同事3倍!
  9. Nachos操作系统-文件系统添加多级目录
  10. matlab 给图片上画框,matlab在图片上画框
  11. 2021全国大学生电子设计竞赛C题
  12. 转行测试,11k入职,我写了份1000多字的分享,每一笔都是经历
  13. 如何求出二次贝塞尔曲线上面的各个点呢
  14. 粉笔科技创新OMO一体化模式 差异化突围职教市场
  15. Btree/B+tree原理及区别(详解)
  16. 获取文件哈希值_图解:什么是哈希?
  17. selenium打开网页被检测,怎么屏蔽和绕过
  18. javascript - js 创建网页 url 桌面快捷方式(兼容chrome)-书签工具
  19. 用 AI 生成简历是怎样的体验? #Rezi AI
  20. MyEclipse的使用小技巧,您get到了吗?

热门文章

  1. python可以干嘛知乎-Python到底可以干什么?老男孩Python视频教程
  2. python里面的之前打过的记忆信息-python 中 __init__方法
  3. python和java哪个-Python与Java-你首选哪个?
  4. php和python交互-PHP与Python进行数据交互
  5. python线下培训-Python培训是应该选择线上还是线下呢?
  6. python自带库处理excel-【Python】处理Excel的库Xlwings
  7. 小学生python-小学生都能学会的python(函数)
  8. TensorRT推理报错:pycuda._driver.LogicError: cuMemcpyHtoDAsync failed: invalid argument
  9. 线上飙升800%,load达到12的解决过程
  10. 行为模式之Chain of Responsibility模式