CMS回收器执行流程
CMS(Concurrent Mark Sweep)
目标:获取最短回收停顿时间为目标的收集器。
算法:"标记-清除"算法实现
CMS是老年代垃圾收集器,在收集过程中可以与用户线程并发操作。
它可以与 Serial 收集器 和 Parallel New收集器搭配使用。
CMS牺牲了系统的吞吐量来追求收集速度,适合追求垃圾收集速度的服务器上。
可以通过JVM启动参数,来开启CMS:
-XX:+UseConcMarkSweepGC
牺牲吞吐量,追求收集速度是什么意思
其实实际使用过程中发现,CMS是将每次收集的时间减少,但是垃圾还是那么多,于是回收的工作方式就变成了跟吃自助餐常听到的一样"勤拿少取",就是每次回收时间短,也并不完全回收全部的垃圾,通过多次回来处理。
1.初始标记(CMS initial mark)
单线程,标记新生代可达老年代的对象。
为了收集应用程序的对象引用需要暂停应用程序线程,该阶段完成后,应用程序线程再次启动。
2.并发标记(CMS-concurrent-mark)
在第一个阶段(Initial Mark)被暂停的应用线程将恢复运行。
通过遍历第一个阶段(Initial Mark)标记出来的存活对象,继续递归遍历老年代,并标记可直接或间接到达的所有老年代存活对象。
这个过程可能存在的问题
应用线程和GC线程是并发执行的,因此可能产生新的对象或对象关系发生变化,例如:
- 新生代的对象晋升到老年代;
- 直接在老年代分配对象;
- 老年代对象的引用关系发生变更;
对于这些对象,需要重新标记以防止被遗漏。
为了提高重新标记的效率,本阶段会把这些发生变化的对象所在的Card标识为Dirty,这样后续就只需要扫描这些Dirty Card的对象,从而避免扫描整个老年代。
3.并发预清理(CMS-concurrent-preclean)
这个阶段就是用来处理:前一个阶段因为引用关系改变导致没有标记到的存活对象的,它会扫描所有标记为Dirty的Card
将会重新扫描前一个阶段标记的 Dirty 对象,并标记被 Dirty 对象直接或间接引用的对象,然后清除Card标识。
也就是让这个标记过后的对象,重新标记为存活。
3.1.可被终止的预清理(CMS-concurrent-abortable-preclean)
本阶段尽可能承担更多的并发预处理工作,从而减轻在Final Remark阶段的stop-the-world。
主要循环的做两件事:
- 处理 From 和 To 区的对象,标记可达的老年代对象;
- 和上一个阶段一样,扫描处理Dirty Card中的对象。
具体执行多久,取决于许多因素,满足其中一个条件将会中止运行:
1.执行循环次数达到了阈值;
2.执行时间达到了阈值;
4.重新标记(CMS Final Remark)
由于第三阶段是并发的,对象引用可能会发生进一步改变。因此,应用程序线程会再一次
被暂停以更新这些变化,并且在进行实际的清理之前确保一个正确的对象引用视图。
这一阶段十分重要,因为必须避免收集到仍被引用的对象。
5.并发清除(CMS concurrent sweep)
所有不再被应用的对象将从堆里清除掉。
6.并发重置 (CMS-concurrent-reset)
状态等待下次CMS的触发
做一些收尾的工作,以便下一次GC周期能有一个干净的状态。这是与用户线程同时运行;
运行状态
了解了几个特点之后,可以实际的看看CMS的运行状态。
上面说到的CMS的几个状态,我们从gc运行日志中去查看CMS的运行状态。
日志中以 CMS 开头的都是CMS各个执行阶段。
这里最需要注意的是:
- CMS Initial Mark
- CMS-remark
这两个阶段分别会暂停应用,也是会让应用程序短暂的暂停也就是STW
。
1 | 2022-01-11T05:48:34.975+0800: 1774622.038: [GC (Allocation Failure) 2022-01-11T05:48:34.975+0800: 1774622.038: [ParNew: 2570680K->58383K(2831168K), 0.0411295 secs] 8357286K->5848239K(9122624K), 0.0414169 secs] [Times: user=0.2 |
CMS缺点
这个不得不吐槽一下,这几个问题,还全让我碰到了。
- CMS收集器对CPU资源非常敏感
- CMS收集器无法处理浮动垃圾
- 浮动垃圾
CMS收集器对CPU资源非常敏感
在并发阶段,它虽然不会导致用户线程停顿,但是会因为占用了一部分线程而导致应用程序变慢,总吞吐量会降低。
这个的实际产生的问题就是,用户线程在处理业务时,由于停顿导致的停顿时间,刚好使业务线程处理时间超时,导致业务处理超时失败。
CMS收集器无法处理浮动垃圾
可能会出现“Concurrent Mode Failure(并发模式故障)”失败而导致Full GC产生。
这个问题,是在检查服务性问题的时候发现的,频率一天几十次,最终调优解决。
浮动垃圾
由于CMS并发清理阶段用户线程还在运行着,伴随着程序运行自然就会有新的垃圾不断产生,这部分垃圾出现的标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC中再清理。这些垃圾就是“浮动垃圾”。
这个就是追求短的低停顿的代价,实际的问题是,内存空间会有大量的对象占用,如果是创建对象比较频繁的应用,就不太友好了,这样也会加大回收的频率。
CMS是一款“标记--清除”算法实现的收集器,容易出现大量空间碎片。当空间碎片过多,将会给大对象分配带来很大的麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。
总结
CMS 是一款低可以实现短停顿的回收器,在停顿时间上确实是比较优秀的,一般来说没有最好的应用,只有最合适的应用,在选择GC时,根据自身的需求进行选择并对细节做过调整,来达到最优的效果。