CMS垃圾收集器详解:初始标记、并发标记、重新标记、并发清除四个阶段中哪些需要Stop The World

在探讨CMS垃圾收集器时,一个关键问题常常被提出:它究竟在哪些阶段会引发“世界暂停”?明确来说,在其标准工作流程的四个阶段中,仅有初始标记和重新标记这两个环节需要触发Stop The World(STW)。这一设计精准地体现了CMS以“最小化延迟”为核心的设计哲学。
初始标记阶段为何必须STW
初始标记阶段可以视作一次高效的“闪电扫描”。其核心任务非常明确:仅标记那些与GC Roots直接相连的第一层对象。具体包括:
- 虚拟机栈(局部变量表)中直接引用的对象
- 本地方法栈中JNI引用的Java对象
- 方法区中静态属性引用的类对象
- 方法区中常量池引用的字符串等对象
由于只需进行一层快速扫描,此阶段的停顿时间极短,通常可控制在毫秒级。但为何必须暂停所有用户线程?根本原因在于确保标记基准的准确性——必须冻结应用状态,获取一个瞬间的、一致的“内存根集快照”。若用户线程仍在运行,根对象集合将处于动态变化中,标记工作便失去了可靠的基础。
并发标记阶段为何无需STW
初始标记完成后,CMS即进入最耗时的并发标记阶段。在此阶段,垃圾收集线程将从已标记的根对象出发,递归遍历整个对象引用链,逐步标记出所有存活的、可达的对象。
此过程的核心优势在于与用户线程并发执行。这意味着应用程序可以持续提供服务,不会因垃圾回收而中断。当然,并发标记在避免停顿的同时,也带来了较长的执行时间,并且会与用户线程竞争CPU计算资源,对系统吞吐量有一定影响。
重新标记阶段为何必须STW
由于并发标记阶段用户线程仍在运行,新的问题随之产生:在漫长的标记过程中,用户线程可能创建了新对象或改变了原有引用关系,导致部分对象出现“漏标”(本应存活却未标记)或“误标”(本应回收却被标记)的情况。
重新标记阶段正是为解决此问题而设。它负责进行最终修正,处理在并发标记期间发生变动的对象引用。此阶段必须再次暂停所有用户线程,以确保能基于一个稳定的内存状态完成最终标记。虽然其耗时略长于初始标记,但相比完整的并发标记,仍是一次非常短暂的强制性暂停。
并发清除阶段为何无需STW
当所有标记工作完成后,便进入最后的清理阶段。在并发清除环节,收集器将依据最终的标记结果,直接回收那些被判定为死亡的对象所占用的内存空间。由于CMS采用经典的“标记-清除”算法,无需移动存活对象,因此整个清理过程可以安全地与用户线程并发进行,不会引发全局性停顿。
总结而言,CMS的设计逻辑清晰而高效:将最耗时的标记与清除任务尽可能置于并发阶段完成,仅保留两次不可避免的、极其短暂的轻量级暂停。这正是它被誉为“低延迟收集器”的核心机制。当然,这种设计也带来了内存碎片、并发模式失败等后续问题,需要在具体应用场景中综合权衡。
