一次CMS GC问题排查过程(理解原理+读懂GC日志)

本文记录了一次因CMS垃圾回收导致的线上问题,包括问题场景、解决过程及GC理论。问题由700M数据引发FullGC,造成系统超时。解决方案包括增大heap区、禁止悲观策略和调整CMS回收比例。文章还介绍了GC的基本原理、主要收集器、新生代和旧生代GC策略,以及何时触发不同类型的GC。
摘要由CSDN通过智能技术生成

这个是之前处理过的一个线上问题,处理过程断断续续,经历了两周多的时间,中间各种尝试,总结如下。这篇文章分三部分:

1、问题的场景和处理过程;2、GC的一些理论东西;3、看懂GC的日志

先说一下问题吧

问题场景:线上机器在半夜会推送一个700M左右的数据,这个时候有个数据置换的过程,也就是说有700M*2的数据在heap区域中,线上系统超时比较多,导致了很严重(严重程度就不说了)的问题。

问题原因:看日志,系统接口超时的时候,系统出现了FullGC,这个时候stop-the-world了,也就停机了。分析gc的日志,发现有promotion failed,根据FullGC触发的条件,这个时候就会出现FullGC了。日志如下:

1
2
2013  -  11  -27T03:  00  :  53.638  +  0800    35333.562  : [GC   35333.562  : [ParNew (promotion failed): 1877376K->1877376K(1877376K),   15.7989680   secs]  35349.361  : [CMS: 2144171K->2129287K(2146304K),   10.4200280   sec
s] 3514052K->2129287K(4023680K), [CMS Perm : 119979K->118652K(190132K)],  26.2193500   secs] [Times: user=  30.35   sys=  5.19  , real=  26.22   secs]

问题解决:中间调整过几次,先搞了几台机器做了验证,后来逐步推广的。

1、调大heap区,由原来的4g,调整到5g,young区的大小不变,还是2g,这时候old区就由2g变为3g了(这样保证old区有足够的空间);

2、设置-XX:UseCMSInitiatingOccupancyOnly,其实这个不关这个问题,只是发现半夜CMS进行的有点频繁,就禁止掉了悲观策略;

3、设置CMS区回收的比例,从80%调整到75%,让old区尽早的进行,有足够的空间剩余;

 

为什么要有GC(垃圾回收)? 

JVM通过GC来回收堆和方法区中的内存,GC的基本原理就是找到程序中不再被使用的对象,然后回收掉这些对象占用的内存。

 

主要的收集器有哪些?

引用计数器和跟踪计数器两种。

引用计数器记录对象是否被引用,当计数器为零时,说明对象已经不再被使用,可以进行回收。java中的对象有复杂的引用关系,不是很适合引用计数器,所以sun jdk中并没有实现这种GC方式。

跟踪收集器,全局记录数据的引用状态,基于一定的条件触发。执行的时候,从根集合开始扫描对象的引用关系,主要有复制(copying)、标记-清除(Mark-Sweep)、标记-压缩(Mark-Compact)那种算法。

 

跟踪计数器的三种算法简介?

复制:从根集合搜扫描出存活的对象,然后将存活的对象复制到一块新的未使用的空间中,当要回收的空间中存活的对象较少时,比较高效;

标记清除:从根集合开始扫描,对存活的对象进行标记,比较完毕后,再扫描整个空间中未标记的对象,然后进行回收,不需要对对象进行移动;

标记压缩:标记形式和“标记清除”一样,但是回收不存活的对象后,会把所有存活的对象在内存空间中进行移动,好处是减少了内存碎片,缺点是成本比较高;

 

java内存区域的形式是啥样的?

这里就不再介绍了,之前有一篇文章中专门介绍这个的( http://iamzhongyong.iteye.com/blog/1333100)。

 

新生代可用的GC?

新生代中对象存活的时间比较短,因此给予Copying算法实现,Eden区域存放新创建的对象,S0和S1区其中一块用于存放在Minor GC的时候作为复制存活对象的目标空间,另外一块清空。

串行GC(Serial GC)比较适合单CPU的情况,可以通过-XX:UseSerialGC来强行制定;

并行回收GC(Parallel Scavenge),启动的时候按照设置的参数来划定Eden/S0/S1区域的大小,但是在运行时,会根据Minor GC的频率、消耗时间来动态调整三个区域的大小,可以用过-XX:UseAdaptiveSizePolicy来固定大小,不进行动态调整;

并行GC(ParNew)划分Eden、S1、S0的区域上和串行GC一样。并行GC需要配合旧生代使用CMS GC(这是他和并行回收GC的不同)(如果配置了CMS GC的方式,那么新生代默认采取的就是并行GC的方式);

 

啥时候会触发Minor GC?

当Eden区域分配内存时,发现空间不足,JVM就会触发Minor GC,程序中System.gc()也可以来触发。

 

旧生代可用的GC方式有哪几种?

串行GC(Serial MSC)、并行GC(Parallel MSC)、并发GC(CMS);

 

关于CMS?

采用CMS时候,新生代必须使用Serial GC或者ParNew GC两种。CMS共有七个步骤,只有Initial Marking和Final Marking两个阶段是stop-the-world的,其他步骤均和应用并行进行。持久代的GC也采用CMS,通过-XX:CMSPermGenSweepingEnabled -XX:CMSClassUnloadingEnabled来制定。在采用cms gc的情况下,ygc变慢的原因通常是由于old gen出现了大量的碎片。

 

为啥CMS会有内存碎片,如何避免?

由于在CMS的回收步骤中,没有对内存进行压缩,所以会有内存碎片出现,CMS提供了一个整理碎片的功能,通过-XX:UseCompactAtFullCollection来启动此功能,启动这个功能后,默认每次执行Full GC的时候会进行整理(也可以通过-XX:CMSFullGCsBeforeCompaction=n来制定多少次Full GC之后来执行整理),整理碎片会stop-the-world.

 

啥时候会触发CMS GC?

1、旧生代或者持久代已经使用的空间达到设定的百分比时(CMSInitiatingOccupancyFraction这个设置old区,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值