cms并发标记阶段的核心目标是让应用线程与gc线程尽可能同时运行以缩短停顿时间,通过初始标记、并发标记(三色标记+写屏障)、重新标记三步协同实现标记一致性,避免漏标和错标。

CMS 的并发标记阶段,核心目标就是让应用(用户)线程和 GC 线程尽可能同时运行,从而大幅缩短停顿时间。它不是“完全不干扰”,而是通过精巧的协作机制,在保证标记准确性前提下实现高并发。
为什么必须协同?不暂停会出问题
如果完全放任用户线程在标记过程中自由修改对象引用,就会出现两类风险:
- 漏标:某个对象原本被引用着,但在并发标记途中被用户线程断开了引用,GC 没来得及标记它,结果被错误回收——这会导致程序崩溃。
- 错标:某个对象本来已不可达(是垃圾),但用户线程在标记中途又给它加了新引用,而 GC 已经把它标为可回收,后续清理就会出错。
所以协同不是为了“不打扰”,而是为了在动态变化中守住标记一致性底线。
初始标记:轻量级握手,快速打点
这是整个流程中第一次 STW(Stop-The-World),但它只做一件事:找出所有 GC Roots 直接关联的对象(比如栈里的局部变量、静态字段等)。这个集合很小,耗时极短,且与堆大小无关,属于“快进快出”的协同起点。
并发标记:边跑边标,靠三色标记+写屏障兜底
GC 线程从初始标记的对象出发,遍历整个对象图并打标;与此同时,用户线程照常分配新对象、修改引用、执行业务逻辑。
为应对引用变更,CMS 采用三色标记法(白-未访问、灰-已访问但子节点未处理、黑-已访问且子节点全处理完),并配合写屏障(Write Barrier)技术:
- 当用户线程修改引用(如把 A.f = B)时,写屏障会拦截该操作;
- 若 B 是白色(未标记)且 A 是黑色(已处理完),就将 B “推回”灰色队列,确保它不会被漏掉;
- 这样即使用户线程在动,GC 线程也能逐步收敛到一个相对准确的可达集。
重新标记:补漏校准,短暂同步
并发标记结束后,用户线程可能又改了几处引用。重新标记阶段再次 STW,但只扫描那些“最可能变动”的区域:
- 被写屏障记录的“脏卡”(Card Table 中标记过的内存页);
- 新生代中刚晋升到老年代的对象;
- 部分 GC Roots 的直接引用(如 JNI 全局引用、线程栈等)。
它不重扫全堆,只聚焦“高危区”,因此停顿远短于初始标记,但比初始标记长一些。
资源协同:CPU 分配要合理
CMS 默认开启的 GC 线程数是 (CPU 核心数 + 3) / 4(向下取整)。这意味着:
- 8 核机器上,CMS 用约 2~3 个线程做并发标记;
- 用户线程仍能占用大部分 CPU,响应不受明显影响;
- 但若只有 2 核,CMS 就可能占掉近一半算力,导致应用吞吐下降明显——这时协同就变成“抢资源”,得权衡是否换 G1 或 ZGC。
不复杂但容易忽略











