并发预清理阶段的核心目的是为最终标记(remark)减负以缩短其stw时间,它基于卡表机制扫描脏卡、提前处理引用变动与浮动垃圾,并主动等待young gc发生。

并发预清理阶段的核心目的,是为最终标记(Remark)做减法,缩短其 STW 时间。它不负责标记新存活对象,而是处理并发标记期间产生的“脏卡”(Dirty Card),提前清理浮动垃圾,并把一部分本该在 Remark 阶段做的扫描工作挪到并发线程中完成。
为什么能缩短最终标记时间?
因为 Remark 必须 STW,且要扫描整个堆(尤其关注年轻代对老年代的引用变化)。如果等 Remark 才去处理所有变动,耗时会飙升。预清理通过以下方式分担压力:
- 扫描并重新标记“脏卡”中已被修改引用的老年代对象,把它们的新可达关系提前确认,避免 Remark 重复扫描整块区域
- 识别并清除那些在并发标记期间已变为不可达、但尚未被回收的浮动垃圾,减少 Remark 需要校验的对象数量
- 主动等待一次 Young GC 发生——若在预清理期间恰好触发了 Minor GC,年轻代变干净,Remark 就不必再花大量时间扫描大量年轻代对象对老年代的引用
它依赖的关键机制是什么?
预清理不是盲扫,而是基于卡表(Card Table)机制运作:
- 当用户线程修改老年代对象的字段(尤其是指向其他老年代对象)时,JVM 的写屏障会将对应内存页所在的“卡”标记为 dirty
- 预清理阶段只遍历这些 dirty 卡,针对性地重新扫描和标记,效率远高于全堆扫描
- 扫描完成后,这些卡被清空(恢复 clean 状态),为下一轮 GC 做准备
实际运行中的关键行为
这个阶段是完全并发的,不暂停应用,但有明确的退出策略:
- 默认最多运行 5 秒,时间一到就结束
- 如果发现老年代空闲空间下降过快(比如 CMSInitiatingOccupancyFraction 快要被突破),会主动中止,防止耽误整体回收节奏
- 日志中频繁出现 Abortable Preclean interrupted,说明触发太晚或老年代分配压力过大,应调低 -XX:CMSInitiatingOccupancyFraction











