分代垃圾回收(进阶)
复习
- 追踪式垃圾回收:从根出发判断可达性
- 时间与空间的交换:用空间换时间
- 局部性:刚用过的、附近的更可能再用
本章为进阶内容,零基础读者可以跳过,不影响后续阅读。
TL;DR
- 大多数对象“朝生夕死”
- 分代 GC 把对象按年龄分成几代
- 优先回收年轻代,减少扫描成本
- 它利用“多数对象很快消亡”的经验
正文
追踪式 GC 很可靠,却有个痛点:每次回收都要暂停程序、扫描大量对象。能不能让它少扫一点、快一点?分代垃圾回收给出了一个漂亮的答案——它建立在一个很实用的经验之上。
一条经验:大多数对象活不久
观察大量真实程序后,人们总结出一条规律(常称分代假说):
- 大多数对象,诞生后很快就死了(临时变量、中间结果……)
- 越老的对象,越可能继续活下去
如果这是真的,那么每次回收都去仔细扫描那些“老资格”对象,就很浪费——它们大概率还要活着。真正值得频繁检查的,是那些刚出生、很可能马上就没用的对象。
按年龄分代
于是,分代 GC 把对象按“年龄”分成几代:
- 年轻代:新创建的对象
- 老年代:熬过了若干次回收、还活着的对象
回收时,优先、频繁地回收年轻代。因为年轻代通常很小,扫起来快;而老年代又大又稳定,就少去扫它。这样,每次回收的成本大大降低。
当一个年轻代对象熬过足够多次回收,就被“晋升”到老年代,从此少被打扰。
它和前面呼应
分代 GC 的背后,还是那条熟悉的局部性经验:刚出现的更可能很快消失、存在久了更可能继续存在。 这和缓存的“时间局部性”、DNS 的 TTL 一样,都是在用“数据在时间上的分布不均”来省力气。
先观察规律,再让机制顺着规律走,往往比一味蛮干高效得多。分代 GC 就是这条思路在内存管理上的一次漂亮应用。
思考题 1
分代 GC 基于什么经验?它带来什么好处?
思考题 2
分代 GC 为什么优先回收年轻代?
小结
知识点
- 分代假说:多数对象很快消亡,越老越可能存活
- 对象按年龄分为年轻代与老年代
- 优先回收年轻代以降低成本
- 熬过多次回收的对象晋升到老年代
参考资料
- Wikipedia(zh):分代垃圾回收:按年龄分代的垃圾回收
- Wikipedia(zh):垃圾回收:自动内存管理的机制
思考题答案(仅供参考)
思考题 1
它基于“大多数对象很快消亡、越老越可能存活”的经验(分代假说)。好处是只频繁扫描很可能已死的年轻代,而不反复检查稳定的老年代,从而显著降低每次回收的成本、缩短停顿。
思考题 2
因为年轻代里的对象大多是刚创建、很可能马上就没用的,回收它们收益高、数量相对少、扫描快。而老年代对象大且通常还要继续存活,频繁扫描不划算。优先回收年轻代正是顺着这条经验省力气。
协议
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
封面图
设计师 | 南国微雪