垃圾回收(GC)是自动内存管理的核心技术,经历了从朴素算法到智能分代的进化。我们以四个里程碑算法为线索,剖析技术演进的底层逻辑:
一、标记-清理(Mark-Sweep):朴素的起点
工作原理:
- 标记阶段:从GC Root出发遍历对象图,标记所有可达对象
- 清理阶段:扫描整个堆内存,回收未被标记的内存块
优势:实现简单,无内存复制开销
致命缺陷:
- 内存碎片:回收产生大量不连续空间,导致内存利用率下降
- 分配延迟:大对象分配时可能需要多次内存搜索
二、标记-复制(Mark-Copy):空间换时间的艺术
核心突破: 将堆划分为两个等大的From和To空间,每次只使用From空间。回收时:
- 标记存活对象
- 将存活对象复制到To空间
- 清空整个From空间
- From和To进行倒置
优势:
- 完全消除内存碎片
- 分配速度极快(只需顺序分配指针)
代价与优化:
- 内存利用率最高仅50%
- 优化方案:IBM研究表明,按8:1比例划分Eden区和Survivor区,内存利用率可达90%
三、标记-整理(Mark-Compact):碎片终结者
操作流程:
- 标记存活对象(同标记-清理)
- 整理阶段:将存活对象向内存一端移动
- 更新对象引用地址
核心价值:
- 兼具标记-清理的内存利用率和标记-整理的连续空间优势
- 适合老年代等存活率高的场景
性能瓶颈:
- 对象移动带来的二次遍历开销
- 对象地址更新导致程序暂停时间(Stop-The-World)增加
四、分代回收(Generational GC):时间维度的革命
理论基础:
- 弱分代假说:绝大多数对象"朝生暮死"
- 强分代假说:经历多次GC的对象更难消亡
代际划分:
区域 | 特点 | 回收算法 | 回收频率 |
---|---|---|---|
新生代 | 98%对象存活时间<1ms | 标记-复制 | 高 |
老年代 | 长期存活对象 | 标记-整理 | 低 |
永久代 | 类元数据等 | 一般不回收 | - |
关键技术:
- 写屏障(Write Barrier):记录跨代引用(图示:跨代引用示意图)
- 卡表(Card Table):以512字节为粒度记录老年代对新生代的引用
五、现代GC的融合之道
实际应用中,各算法形成组合拳:
- G1收集器:将堆划分为多个Region,动态决定各区域的回收策略
- ZGC:基于染色指针实现并发整理
- Shenandoah:通过读屏障实现并发复制
不同语言运行时根据场景选择策略:
- Go语言:追求低延迟,采用标记-清理+分代式
- Java HotSpot:平衡吞吐与延迟,G1作为默认收集器