标记-整理算法先标记存活对象,再将其紧凑移动至内存一端以消除碎片;包含标记、计算新地址、更新引用、移动对象四阶段,兼顾内存利用率与碎片控制。

标记-整理算法(Mark-Compact Algorithm)是垃圾回收中一种兼顾内存利用率与碎片控制的策略,核心思想是先标记存活对象,再将它们紧凑地移动到内存一端,从而消除碎片、提升后续分配效率。
标记阶段:识别所有可达对象
从根集合(如栈帧、全局变量、寄存器等)出发,遍历所有引用链,递归标记所有可达对象。通常使用深度优先或广度优先遍历,每个对象通过一个标记位(如 header 中的 flag)记录是否存活。
- 标记过程必须保证不遗漏——任何被根直接或间接引用的对象都需标记
- 需注意循环引用:现代标记逻辑天然支持,无需额外处理
- 并发标记时需配合写屏障(Write Barrier)维持一致性
计算新地址阶段:为存活对象规划连续空间
标记完成后,按内存起始地址顺序扫描,累计已标记对象大小,为每个对象计算其在整理后的新起始地址。本质是构建“偏移映射表”。
- 从堆底开始累加,第一个存活对象新地址 = 堆起始地址
- 第二个存活对象新地址 = 第一个对象新地址 + 其大小
- 该阶段不移动数据,仅做地址预演,确保紧凑布局可行
更新引用阶段:修正所有指向旧地址的指针
由于对象位置改变,所有指向这些对象的引用(包括栈、寄存器、其他对象字段)必须更新为新地址。这是标记-整理最耗时的环节之一。
- 需遍历所有根引用和对象内部引用字段
- 常借助“转发指针(forwarding pointer)”优化:在原对象头中暂存新地址,避免重复查找
- 若支持并发,需暂停 mutator(Stop-The-World)或采用读屏障/写屏障协同
移动阶段:按序搬运对象至目标位置
最后,按新地址顺序将所有存活对象逐个复制过去。移动顺序必须与地址递增一致,防止覆盖未搬移的数据。
- 从高地址向低地址移动可避免覆盖;也可从低到高,但需确保源与目标无重叠
- 移动后清空原位置标记,并重置对象头信息(如哈希码、锁状态等)
- 移动完成即释放剩余内存空间,堆变为一段连续可用区域
标记-整理在减少碎片的同时带来移动开销和引用更新成本,适合堆较大、存活率中等、且对内存连续性要求高的场景。相比复制算法节省空间,相比标记-清除避免碎片,是一种折中而稳健的设计。

















