标记-整理算法是老年代垃圾回收的主力策略,通过标记存活对象、向内存一端紧凑移动、清理边界外空间三步消除碎片,兼顾内存利用率与碎片控制,适用于高存活率场景。

标记-整理算法是老年代(Old Generation)垃圾回收的主力策略,尤其在 Serial Old、Parallel Old 等经典收集器中被直接采用。它不追求极致吞吐或最低停顿,而是在存活率高、碎片敏感的场景下,稳住内存结构——这正是老年代最典型的特点。
为什么老年代适合用标记-整理
老年代对象普遍存活时间长、密度高、移动成本虽高但不可避免。若用标记-清除,几次GC后就会堆满细碎空洞,大对象分配易触发 Full GC;若用复制算法,则需预留近一倍空间,严重浪费——而标记-整理在二者间取了务实平衡:
- 存活对象多 → 不适合频繁复制(复制算法低效)
- 对象体积大、分布散 → 清除后极易碎片化(标记-清除不可持续)
- GC频次低、但每次影响大 → 可接受单次较长停顿,换长期内存规整
实战中它怎么跑完一次完整回收
一次典型的标记-整理过程分三步落地,每步都直接影响应用表现:
- 标记阶段:从 GC Roots(如线程栈、静态字段、JNI 引用)出发,递归遍历并标记所有可达对象。此阶段需 Stop-The-World,但耗时与存活对象数正相关,而非整个堆大小
- 计算新地址:扫描堆内存,按顺序为每个存活对象规划紧凑排列的新起始位置(例如从低地址端连续排布),同时记录映射关系
- 移动+更新引用:将对象逐个拷贝到新位置,并同步修正所有指向它的引用(包括栈中局部变量、其他对象字段等)。这是最耗时环节,也是 STW 持续的主要原因
哪些参数和现象能帮你判断它正在工作
运行时可通过 JVM 日志确认标记-整理的实际触发与效果:
- 开启 -XX:+PrintGCDetails -Xlog:gc* 后,看到类似
SerialOld: [MarkSweep]或PSOldGen: [PSMarkSweep]即表示该次 GC 使用了标记-整理逻辑 - 日志中若出现
compact、defrag、moving objects等关键词,基本可断定整理阶段已执行 - 对比 GC 前后,老年代使用量下降明显,且
used值趋近于capacity × 存活率(无显著碎片残留),说明整理有效
调优时要注意的实际限制
标记-整理不是“万能解”,实战中常受限于以下现实约束:
- 它无法并发执行核心移动逻辑——所有应用线程必须暂停,因此对延迟敏感系统(如实时交易)需谨慎选用
- 对象若含大量本地句柄(如 DirectByteBuffer 底层内存)、JNI 全局引用,移动时需额外同步处理,可能延长 STW
- 当老年代剩余空间不足以容纳整理后的存活对象(即“压缩失败”),会退化为标记-清除,甚至触发 Full GC
- 在 G1 或 ZGC 等新型收集器中,标记-整理思想被拆解重构(如 G1 的混合回收 + Region 内复制),不再以传统形式出现











