简述 CMS 垃圾收集器的工作流程,它有什么优缺点?
CMS(Concurrent Mark Sweep,并发标记 - 清除)是 JVM 中针对老年代的垃圾收集器,核心目标是 “减少 STW(Stop The World)时间”,适用于高并发、对响应时间敏感的应用(如电商网站、金融交易系统),工作流程和优缺点如下:
# 1. CMS 垃圾收集器的工作流程
CMS 基于 “标记 - 清除” 算法,将 GC 过程分为 4 个步骤,其中 2 个步骤与用户线程并发执行,仅 2 个步骤需要 STW,大幅缩短 STW 时间:
# (1)初始标记(Initial Mark,STW)
- 目标:标记 GC Roots 直接引用的老年代对象(如线程栈引用、静态变量引用的老年代对象);
- 过程:暂停所有用户线程(STW),快速遍历 GC Roots,标记直接引用的老年代对象;
- 特点:STW 时间极短(通常毫秒级),因为仅标记 “直接引用”,不遍历引用链。
# (2)并发标记(Concurrent Mark,非 STW)
- 目标:从初始标记的对象出发,遍历老年代的所有对象引用链,标记所有存活对象;
- 过程:恢复用户线程,GC 线程与用户线程并发执行,遍历老年代的对象引用,标记存活对象;
- 特点:无 STW,不影响用户线程响应性,但会占用 CPU 资源(默认 GC 线程数为
(CPU核心数 + 3) / 4),可能导致应用吞吐量下降。
# (3)重新标记(Remark,STW)
- 目标:修正并发标记期间因用户线程操作导致的 “标记变动”(即 “浮动垃圾” 的反向问题);
- 背景:并发标记时,用户线程可能修改对象引用(如删除引用、新增引用),导致部分对象的标记状态不准确(如并发标记时标记为存活,但用户线程后续断开引用,变为死亡对象;或并发标记时未标记,但用户线程新增引用,变为存活对象);
- 过程:再次暂停所有用户线程(STW),通过 “Card Table”(记录老年代对象引用变动的表)快速找到变动的对象,修正标记状态;
- 特点:STW 时间比初始标记长(通常几十毫秒),但远短于 Serial Old 收集器的 STW 时间;JDK 6 后引入 “增量重新标记”(Incremental Remark),进一步缩短 STW 时间。
# (4)并发清除(Concurrent Sweep,非 STW)
- 目标:清除老年代中未被标记的死亡对象,释放内存;
- 过程:恢复用户线程,GC 线程与用户线程并发执行,遍历老年代,清除未标记的对象;
- 特点:无 STW,不影响用户线程,但会产生内存碎片(标记 - 清除算法的固有问题);同时,并发清除期间用户线程产生的新死亡对象(“浮动垃圾”)无法被本次 GC 回收,需等待下次 GC。
# 2. CMS 垃圾收集器的优点
- 1. 低 STW 时间:仅初始标记和重新标记需要 STW,总 STW 时间通常在 10-100 毫秒,远低于 Serial Old(秒级)和 Parallel Old(百毫秒 - 秒级)收集器,适合对响应时间敏感的应用;
- 2. 并发执行:并发标记和并发清除与用户线程并发执行,用户线程无需长时间等待,应用响应性好;
- 3. 吞吐量较高:虽然并发执行会占用 CPU 资源,但相比 Serial Old(单线程 GC),CMS 的 GC 效率更高,应用吞吐量(单位时间处理请求数)损失较小;
- 4. 适配老年代特性:老年代对象存活时间长,死亡对象少,标记 - 清除算法的 “标记成本低”,适合 CMS 的并发标记流程。
# 3. CMS 垃圾收集器的缺点
- 1. 产生内存碎片:基于标记 - 清除算法,并发清除后老年代会产生大量不连续的空闲内存块;当需要分配大对象时,可能因找不到足够大的连续空闲块而触发 “Full GC 降级”(即用 Serial Old 收集器进行标记 - 整理,STW 时间长);
- 2. 产生浮动垃圾:并发清除期间,用户线程产生的新死亡对象无法被本次 GC 回收,称为 “浮动垃圾”;若浮动垃圾过多,可能导致老年代空间提前满,触发下一次 GC(甚至 FGC 降级);
- 3. CPU 资源敏感:并发标记和并发清除需要 GC 线程与用户线程抢占 CPU 资源,当 CPU 核心数较少(如 2 核)时,GC 线程会占用较多 CPU(默认
(2+3)/4=1个核心),导致应用吞吐量下降; - 4. 无法处理大堆内存:当老年代内存过大(如超过 8GB)时,并发标记和重新标记的时间会显著增加,STW 时间无法控制,此时推荐使用 G1 收集器;
- 5. 老年代 GC 触发时机难把握:CMS 默认在老年代内存使用率达到
-XX:CMSInitiatingOccupancyFraction(默认 92%)时触发 GC;若触发过晚,可能因老年代空间不足导致 FGC 降级;若触发过早,会增加 GC 频率,浪费 CPU 资源。
# 4. CMS 的适用场景与替代方案
- 适用场景:堆内存中等(老年代 < 8GB)、对响应时间敏感、CPU 核心数充足(4 核及以上)的应用(如电商订单系统、支付系统);
- 替代方案:JDK 9 后 CMS 被标记为 deprecated(过时),推荐用G1 收集器替代;G1 支持分区收集,兼顾低 STW 时间和无内存碎片,适配大堆内存(如 10GB-100GB)。
上次更新: 12/30/2025