这是「一文拿下 GC」系列第三篇。判定对象「该死」后,下一步是怎么回收。三大经典算法——标记-清除、复制、标记-整理——各有取舍,现代收集器都是它们的组合或变种。
一、标记-清除算法(Mark-Sweep)
1.1 核心思想
算法分为「标记」和「清除」两个阶段:
- 标记阶段:从 GC Roots 出发,标记所有可达对象为「存活」
- 清除阶段:统一回收所有未被标记的对象(即不可达对象)
这是最「朴素」的算法——其他两个算法都是它的改进版。
1.2 缺点
效率问题:
- 标记和清除两个过程的效率都不高
- 标记需要遍历对象图,清除需要遍历整个堆
空间问题:
- 标记清除之后产生大量不连续的空间碎片
- 空间碎片过多可能导致:以后程序运行时需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作
后果:明明堆还有 30% 空闲,但放不下一个 20% 的连续大对象——只能再 GC 一次。
1.3 适用场景
- 早期 JVM
- CMS 收集器的老年代回收(CMS 是 Mark-Sweep 的代表实现,但有碎片问题)
- 对内存连续性要求不高的场景
二、复制算法(Copying)
2.1 核心思想
将内存分为大小相等的两块,每次只使用其中一块:
- 这块内存用完时,将存活的对象复制到另一块
- 然后将已使用过的内存空间一次性清理
- 内存分配时也无需考虑内存空间碎片等复杂情况
- 只需要移动堆顶指针按顺序分配内存即可
- 实现简单,运行高效
2.2 缺点
- 将内存缩小为原来的一半(一半永远空闲)
2.3 HotSpot 的优化:Eden + Survivor
IBM 公司专门研究表明,新生代对象 98% 是「朝生夕死」——所以无需按照 1:1 分配内存空间。
HotSpot 把内存分为:
- 一块较大的 Eden 空间
- 两块较小的 Survivor 空间(S0 + S1)
每次使用 Eden 和其中一块 Survivor:
- GC 时把存活对象从 Eden + 当前 Survivor 复制到另一块 Survivor
- 然后清空 Eden + 刚用过的 Survivor
- 角色互换
HotSpot 默认的 Eden 和 Survivor 大小比例为 8:1——也就是整个新生代可用容量为新生代总容量的 90%(80% Eden + 10% Survivor),只浪费 10%。
2.4 分配担保(Handle Promotion)
问题:没办法保证每次回收都有不多于 10% 的对象存活。
解决:当 Survivor 空间不够用时,需要依赖老年代内存进行分配担保(Handle Promotion)——多余的存活对象直接通过分配担保机制进入老年代。
2.5 适用场景
- 对象存活比例较低的内存区域(新生代就是典型)
- 如果对象存活比例较高,将存活对象复制到另一个 Survivor 将会耗费更多的时间,降低效率——不适合老年代
三、标记-整理算法(Mark-Compact)
3.1 核心思想
适合存活率较高的老年代:
- 标记的过程和标记-清除的标记过程一样
- 后续步骤不是直接对可回收对象进行清理,而是让所有存活对象向一端移动
- 然后直接清理掉端边界以外的内存

3.2 关键细节
- 注意:GC 移动对象,需要修改所有(堆和栈中)指向该对象的引用
- 移动/提升/压缩是 STW 的(Stop The World),所有修改对象的引用是安全的
- 不需要额外的空闲列表或碎片管理
3.3 适用场景
- 老年代(对象存活率高,复制算法不划算)
- Parallel Old 收集器的老年代回收
- G1 收集器的 region 整理
- 任何对内存连续性有要求的场景
四、三大算法对比
| 维度 | 标记-清除 | 复制 | 标记-整理 |
|---|---|---|---|
| 速度 | 中等(标记+清除都需遍历) | 最快(只复制存活对象) | 较慢(标记+移动) |
| 空间开销 | 少(只需标记位) | 大(浪费 50% 或 10%) | 少(原地整理) |
| 碎片 | 严重 | 无 | 无 |
| 移动对象 | 不移动 | 移动 | 移动 |
| 适用区域 | 老年代(CMS) | 新生代 | 老年代 |
| STW | 较短 | 较短 | 较长(需要更新引用) |
五、现代收集器的算法组合
实际生产中,没有收集器只用一种算法——都是混合:
- 新生代:复制算法(Eden + Survivor)
- 老年代:
- CMS = 标记-清除(碎片多,但停顿短)
- Parallel Old = 标记-整理(停顿长,但无碎片)
- G1 = 整体标记-整理 + 局部 region 复制(region 之间复制)
2017 年冷知识:当年 G1 已经是 HotSpot 的「未来之星」,但生产环境大量跑的还是 Parallel Old(吞吐量优先)或 CMS(停顿优先)。2017 年 G1 还在收集器架构革命期,很多参数和 region 策略都在变动。
小结
- 标记-清除:朴素但有碎片问题,CMS 用它
- 复制算法:快但费空间,新生代标配(Eden 8 + Survivor 1)
- 标记-整理:无碎片但 STW 较长,老年代标配
下一篇讲这些算法在生产中的实际载体——七大垃圾收集器(Serial/ParNew/Parallel Scavenge/Serial Old/Parallel Old/CMS/G1)的工作原理和适用场景。