记一次 CMS GC导致 FULL GC 时间开销很大的排查

背景

  1. 服务接入注册中心后,就会有实例健康检查,通过ip+port的方式访问接口,检查实例是否健康,某日有个实例出现了告警。检查发现是当时接口超时异常了,触发告警。
  2. 短暂的时间后,服务又正常恢复了,接口正常响应。观察日志没有异常问题。

定位分析过程

  1. 这个健康接口是个简单的返回,没有经过DB、缓存等。所以超时问题出现在服务本身。
  2. 查看CAT、机器基础监控,检查内存,CPU,GC等监控,发现FULL GC在这个时间点有个很长的耗时,环比其他时间都没有这个现象。
  3. 定位是GC 导致 时间很长,服务暂停,导致接口超时。

第一次尝试解决方案

  1. 分析:服务的JVM参数是 -xms2g -xmx5g
  2. 考虑到FULL GC时间长,那应该是老年代需要回收的垃圾太多了,导致整个GC耗时长了,所以调整-xmx4g,缩小JVM的大小,老年代尽快完成FULl GC.
  3. 效果不明显,还是会出现这个FULL GC耗时长,接口超时告警. 进一步分析JVM,服务使用的是CMS GC收集器,考虑这里有个STW的阶段可能是原因。

CMS GC收集器分析

了解CMS收集原理

CMS(Concurrent Mark Sweep)收集器是 HotSpot 虚拟机第一款真正意义上的并发收集器,多线程标记清除算法,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。

从名字中的Mark Sweep这两个词可以看出,CMS 收集器是一种 “标记-清除”算法实现的。整个过程分为四个步骤:

  1. 初始标记: 暂停所有的其他线程,并标记一下 GC Roots 能直接关联的对象,速度很快 ;
  2. 并发标记: 同时开启 GC 和用户线程,用一个闭包结构去记录可达对象。但在这个阶段结束,这个闭包结构并不能保证包含当前所有的可达对象。因为用户线程可能会不断的更新引用域,所以 GC 线程无法保证可达性分析的实时性。所以这个算法里会跟踪记录这些发生引用更新的地方。
  3. 重新标记: 需要暂停所有的工作线程,为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录。
  4. 并发清除: 开启用户线程,同时 GC 线程开始对未标记的区域做清扫。

优缺点分析总结

它是一款优秀的垃圾收集器,主要优点:并发收集、低停顿。但是它有下面的缺点:

  1. 无法处理浮动垃圾;
  2. 回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生。

分析根因

  1. 项目中大量使用了本地缓存,使用 guava cache 缓存了很多数据。
  2. 对于CMS垃圾收集器,如果是大量本地缓存应用的话,重新标记阶段这个耗时就会较长,因为在并发阶段很容易有很多新对象进入缓存,从而重新标记阶段扫描很耗时,并且重新标记阶段是STW,暂停所有的工作线程。
  3. 当刚好进行并发阶段,同时缓存正在重新加载刷新的时候,就好会出现这个问题。

解决方案

  1. 缓存设计上,减少不必要的缓存,如一些冷数据不再缓存。
  2. 开启-XX:CMSScavengeBeforeRemark,在重新标记阶段前进行一次YGC,有利于减少 Young Gen 对 Old Gen 的无效引用,从而减少重新标记阶段扫描GC Roots的开销。
  3. 尝试使用G1垃圾收集器,通过-XX:MaxGCPauseMillis设置最大停顿时间,提高服务可用性。
  4. 考虑团队用的JVM 收集器都是CMS,本次优化是减少缓存的使用,同时开启 -XX:CMSScavengeBeforeRemark

服务使用大量本地缓存,CMS GC收集器重新标记阶段导致STW时间很长。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值