cms收集器的四大核心步骤是初始标记(stw,快速标记gc roots直达对象)、并发标记(与用户线程并发,遍历引用链)、重新标记(stw,修正并发变动)、并发清除(并发回收未标记对象)。

CMS收集器的四大核心步骤,本质是围绕“低停顿”目标,在标记-清除算法框架下,把原本需要全程STW(Stop-The-World)的工作拆解为尽量并发执行的阶段。关键不在于步骤数量,而在于哪些阶段必须暂停用户线程、哪些可以并行运行,以及各阶段要解决什么实际问题。
初始标记:快速抓取根直达对象
这个阶段只标记GC Roots直接关联的对象(比如线程栈里的局部变量、静态字段、JNI引用等),不递归遍历。范围小、速度快,通常几毫秒到百毫秒内完成,但必须STW。它为后续并发标记提供一个可靠的起点集合——即“已知存活”的种子对象。
- GC Roots包括:虚拟机栈中引用的对象、方法区中的静态变量、本地方法栈中的JNI引用、被同步锁持有的对象
- JDK 8起默认启用并行初始标记(可通过-XX:+CMSParallelInitialMarkEnabled显式控制)
- 若老年代很大但GC Roots极少(如大量静态常量未引用新对象),该阶段依然很轻量
并发标记:边跑业务边画存活图
从初始标记得到的“种子”出发,沿着所有引用链遍历整个老年代对象图,逐个标记可达对象。这个过程与用户线程完全并发,不暂停应用,但耗时最长(可能几百毫秒到数秒),且会占用CPU资源。
- 标记过程本身不修改堆结构,只更新对象头的mark bit或使用额外位图
- 用户线程在此期间可能修改引用关系(如赋值、断引用),导致部分对象状态“滞后”
- 这是CMS能大幅降低总停顿时间的主因——最耗时的部分交给了并发执行
重新标记:补漏,应对并发带来的变动
由于并发标记期间用户线程持续运行,对象引用关系可能发生变化(例如A指向B,之后A改为指向C,B就可能变成垃圾但未被标记)。重新标记阶段会STW,扫描那些在并发标记中“逃逸”的变动,确保标记结果准确。
- 主要扫描两类区域:被用户线程修改过的卡表(Card Table)、年轻代中晋升到老年代的新对象
- 耗时比初始标记长,但远短于并发标记;可通过-XX:+CMSScavengeBeforeRemark提前清理年轻代,减少需扫描范围
- 这是CMS中停顿时间最长的一次STW,优化重点常落在缩短它上
并发清除:边清垃圾边继续服务
根据前两轮标记的结果,直接回收所有未被标记的对象内存。不移动存活对象,所以不需要更新引用地址,可全程与用户线程并发执行。
- 清除操作只是将内存块加入空闲链表或更新空闲位图,开销相对可控
- 清除过程中产生的新垃圾(如用户线程刚创建又丢弃的对象)称为“浮动垃圾”,只能等到下次GC周期处理
- 因不整理内存,长期运行易产生碎片;当无法分配大对象时,可能触发Full GC(退化为Serial Old)











