标记-整理算法通过标记存活对象、向一端移动并更新引用、清理边界外内存,解决老年代碎片问题且无需额外空间;适用于高存活率场景,但移动开销大、stw时间长。

标记整理算法(Mark-Compact Algorithm)是 Java 垃圾回收中一种兼顾内存利用率和碎片控制的回收策略,主要用在老年代(Old Generation),比如 Serial Old 和 Parallel Old 收集器就采用该算法。
标记整理算法的核心思想
它分为两个阶段:先标记所有存活对象,再将它们向内存一端移动,最后清理边界外的内存空间。相比标记清除算法,它解决了内存碎片问题;相比复制算法,它避免了额外的内存空间开销。
关键在于“整理”——不是简单地清空死亡对象,而是通过移动让存活对象紧凑排列,从而释放出连续的大块空闲内存。
标记整理算法的执行步骤
- 标记阶段:从 GC Roots 出发,遍历并标记所有可达对象(与标记清除、标记复制一致)
- 整理阶段:将所有存活对象按地址顺序(通常从起始位置开始)依次搬移到内存的同一侧,例如堆的低端
- 清理阶段:更新对象引用地址后,直接将整理边界之后的整段内存设为空闲,无需逐个清除
为什么老年代更适合用标记整理?
老年代对象生命周期长、存活率高,复制算法需要大量复制操作,效率低且浪费空间;而标记清除虽快,但易产生碎片,影响大对象分配(如数组)。标记整理在两者间取得平衡:
- 不依赖额外的半区空间,适合老年代这种空间大、变动少的区域
- 整理后内存连续,支持快速的“指针碰撞”方式分配新对象
- 虽然移动对象需更新引用(包括栈、寄存器、其他对象中的引用),但现代 JVM 通过写屏障和卡表等机制高效处理
实际应用中的优化细节
纯标记整理算法存在性能瓶颈(比如频繁移动、引用更新开销大),因此 JVM 实现通常做了优化:
- 部分整理(如 G1 的 Region 级整理):只对回收价值高的区域进行整理,而非整个老年代
- 双指针滑动整理(如 Serial Old):使用“L”(left)和“R”(right)两个指针,一边扫描存活对象,一边向左搬运并更新指针
- 结合压缩(Compaction)策略:在整理时同步调整对象大小或合并相邻空闲块,进一步提升利用效率
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











