本节摘要:判死之后怎么清,只有三种基本术式:标记-清除原地释放但留碎片,标记-复制搬走活者但费空间,标记-整理滑动压实但搬运有成本。本节用同一批对象的三种命运画清三者的过程与代价,再解释分代策略如何按区域特征组合三种术式——第五章后面两节的收集器设计全部由此推导。
5.1 回答了"谁该死",本节回答"怎么清"。三术式的差别不是风格差异,而是三笔账的取舍:释放要不要搬东西、空间损失多少、碎片怎么处理。把这三笔账算清,任何收集器的宣传词都能翻译回成本语言。
过程两步:从 Roots 标记可达对象;然后线性扫描堆,把未标记对象的内存原地登记为空闲。它不打扰幸存者——这是最大优点(对象不动,引用不需要修正,停顿内的工作量正比于堆大小)。
代价是碎片:死对象腾出的空洞大小不一、位置零散。日积月累,堆的总空闲足够、却没有一块连续空间能容纳大对象,只能触发一次回收甚至提前 Full GC。碎片还会拖慢分配:空闲列表式分配(维护空洞账本)比"指针碰撞"式分配慢,且空洞越碎账本越长。CMS 收集器选择清除术,"碎片积累"正是它致命的慢性病之一。
过程:把空间对半分,只用其中一半;回收时把活对象全部搬进另一半,然后整块清空用过的那半。碎片天然为零(活对象紧凑排列),分配退化成指针碰撞——挪个指针就是分配,快到极致。
代价是空间利用率减半,以及"搬运量正比于存活对象":死亡率越高越划算,活对象越多越亏。新生代恰好满足"朝生暮死"(第二章弱分代假说),所以复制术是新生代的默认选择——而且不必真的对半切:Eden 与两块 Survivor 的八比一比一布局,本质是"九成空间干活、一成做搬运目的地"的复制术改良版,空间浪费被压到一成。
过程:标记之后,把所有活对象向空间一端滑动,压实后清掉边界外的全部。既无碎片,也不浪费一半空间,是老年代这种"存活率高、不能忍受空间减半"区域的天然候选。
代价是搬运本身:对象要移动、指向它们的引用要全部修正,且滑动过程难以完全并发——这就是为什么"整理"长期与明显停顿绑定,也是后来 Shenandoah、ZGC 花大力气研发并发整理的原因。三种术式的全程对比如图:

术式差异能在 GC 日志里直接看出痕迹。同样制造一批朝生暮死的对象,分别用新生代复制术与整堆整理术观察(实验机数字会有出入,看结构):
public class Churn { public static void main(String[] args) { for (int i = 0; i < 5_000_000; i++) { byte[] dust = new byte[256]; // 高频短命对象 } System.out.println("done"); } }
$ java -Xms64m -Xmx64m -XX:+UseSerialGC -Xlog:gc Churn [0.091s] GC(0) Pause Young (Allocation Failure) Eden: 18064K->0K(18432K) [0.094s] GC(1) Pause Young (Allocation Failure) Eden: 18432K->0K(18432K) ... $ java -Xms64m -Xmx64m -XX:+UseSerialGC -Xlog:gc* Churn 2>&1 | grep -c "Full" 2
新生代日志只出现 Eden 归零的年轻回收,几乎不碰老年代——复制术在死亡率九成以上的区域如鱼得水。若把同样的 churn 压力改成"每轮留住一批对象"(模拟高存活率),就会发现年轻回收撑不住、晋升加快、最终走向 Full GC 整理——术式与负载特征的错配,是很多"莫名 Full GC"的根源。这套读日志的完整方法在 5.4 展开,这里先建立"术式可从日志痕迹反推"的意识。
现在可以正面回答"为什么分代"了。三种术式各有一个甜蜜区:复制术要求死亡率高,清除术不挪对象但留碎片,整理术无碎片但搬运贵。而堆内不同区域的天然特征恰好互补——新生代死亡率极高(复制术),老年代存活率高、搬运代价敏感(清除或整理术)。分代不是规范要求,而是三术式成本模型推导出的工程最优解。
顺着这个视角再看收集器差异就非常轻松了:CMS 新生代复制、老年代清除(所以要并发"预清理"缓解碎片);G1 整体仍是分代逻辑但把堆切成 Region,Region 之间用复制,大对象单独安置;ZGC 走"不分代的并发整理"路线(新版本引入分代支持),靠着色指针让搬运也能并发。5.3 节的家族巡礼,不过是这三笔账在不同硬件年代的重新结算。
⚠️ 常见坑:以为"碎片只影响大对象分配"。碎片还会放大回收频率——空闲总量够但连续性差,分配失败提前触发回收;在 CMS 时代更是直接倒逼 Full GC。看到"老年代占用不高却频繁 Full"的日志,碎片是头号嫌疑。
💡 关键直觉:三种术式对应三笔账——碎片账、空间账、搬运账。任何收集器的任何设计,都能还原成"在哪块区域、怎么付这三笔账"。记住等式再看宣传材料,永远不会被参数名唬住。
三术式与分代组合的原理备齐,下一节进入家族史:从 Serial 到 ZGC,历代收集器分别在向谁开刀、解决了上一代的什么病——以及新版本里你该默认用谁。