Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

分代垃圾回收(进阶)

复习

  • 追踪式垃圾回收:从根出发判断可达性
  • 时间与空间的交换:用空间换时间
  • 局部性:刚用过的、附近的更可能再用

本章为进阶内容,零基础读者可以跳过,不影响后续阅读。

TL;DR

  • 大多数对象“朝生夕死”
  • 分代 GC 把对象按年龄分成几代
  • 优先回收年轻代,减少扫描成本
  • 它利用“多数对象很快消亡”的经验

正文

  追踪式 GC 很可靠,却有个痛点:每次回收都要暂停程序、扫描大量对象。能不能让它少扫一点、快一点?分代垃圾回收给出了一个漂亮的答案——它建立在一个很实用的经验之上。

一条经验:大多数对象活不久

  观察大量真实程序后,人们总结出一条规律(常称分代假说):

  • 大多数对象,诞生后很快就死了(临时变量、中间结果……)
  • 越老的对象,越可能继续活下去

  如果这是真的,那么每次回收都去仔细扫描那些“老资格”对象,就很浪费——它们大概率还要活着。真正值得频繁检查的,是那些刚出生、很可能马上就没用的对象。

按年龄分代

  于是,分代 GC 把对象按“年龄”分成几代:

  • 年轻代:新创建的对象
  • 老年代:熬过了若干次回收、还活着的对象

  回收时,优先、频繁地回收年轻代。因为年轻代通常很小,扫起来快;而老年代又大又稳定,就少去扫它。这样,每次回收的成本大大降低。

  当一个年轻代对象熬过足够多次回收,就被“晋升”到老年代,从此少被打扰。

它和前面呼应

  分代 GC 的背后,还是那条熟悉的局部性经验:刚出现的更可能很快消失、存在久了更可能继续存在。 这和缓存的“时间局部性”、DNS 的 TTL 一样,都是在用“数据在时间上的分布不均”来省力气。

  先观察规律,再让机制顺着规律走,往往比一味蛮干高效得多。分代 GC 就是这条思路在内存管理上的一次漂亮应用。

思考题 1

  分代 GC 基于什么经验?它带来什么好处?

思考题 2

  分代 GC 为什么优先回收年轻代?

小结

知识点

  • 分代假说:多数对象很快消亡,越老越可能存活
  • 对象按年龄分为年轻代与老年代
  • 优先回收年轻代以降低成本
  • 熬过多次回收的对象晋升到老年代

参考资料

  1. Wikipedia(zh):分代垃圾回收:按年龄分代的垃圾回收
  2. Wikipedia(zh):垃圾回收:自动内存管理的机制

思考题答案(仅供参考)

思考题 1

  它基于“大多数对象很快消亡、越老越可能存活”的经验(分代假说)。好处是只频繁扫描很可能已死的年轻代,而不反复检查稳定的老年代,从而显著降低每次回收的成本、缩短停顿。

思考题 2

  因为年轻代里的对象大多是刚创建、很可能马上就没用的,回收它们收益高、数量相对少、扫描快。而老年代对象大且通常还要继续存活,频繁扫描不划算。优先回收年轻代正是顺着这条经验省力气。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪