这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天
今天讲了编程语言中常用的几种 GC 方案
- 追踪垃圾回收
- 对象回收条件:指针指向关系不可达
- 标记根对象
- 静态变量、全局变量、常量、线程栈等
- 标记:找到可达对象
- 清理:所有不可达对象
- 将存活对象复制到另外的内存空间(Copying GC)
- 将死亡对象的内存标记为“可分配"(Mark-sweep GC)
- 移动并整理存活对象(Mark-compact GC)
- 根据对象的生命周期。使用不同的标记和清理策略
- 分代 GC (Generational GC)
比较容易联想到 JVM 中的 GC 机制
-
分代假说(Generational hypothesis): most objects die young
-
Intuition:很多对象在分配出来后很快就不再使用了
-
每个对象都有年龄:经历过GC的次数
-
目的:对年轻和老年的对象,制定不同的GC策略,降低整体内存管理的开销
-
不同年龄的对象处于heap的不同区域
- 年轻代 (Young generation)
- 常规的对象分配
- 由于存活对象很少,可以采用copying collection(指拷贝到连续内存,减少内存碎片,方便统一管理)
- GC吞吐率很高
-
老年代(Old generation)
- 对象趋向于一直活着,反复复制开销较大
- 可以采用 mark-sweep collection
-
引用计数
- 参考 C++ 的智能指针
-
Go 的三色标记算法
Go从1.5版本实现了基于三色标记清除的并发垃圾收集器,能在不暂停程序的情况下即可完成对象的可达性分析,算法将程序中的对象分成白色、黑色和灰色三类:
- 白色对象 - 潜在垃圾,表示还未搜索到的对象,其内存可能会被垃圾收集器回收;
- 黑色对象 - 活跃对象,表示搜索完成的对象,包括不存在任何引用外部指针的对象以及从根对象可达的对象
- 灰色对象 - 活跃对象,表示正在搜索还未搜索完的对象,因为存在指向白色对象的外部指针,垃圾收集器会扫描这些对象的子对象;
实现步骤:
- 全部对象初始标记为白色
- 从 gc root 出发,将可达对象标记为灰色,放入处理队列
- 每次从队列中取一个灰色对象标记为黑色,并把该对象的引用对象标记为灰色,放入处理队列
- 重复标记直到灰色队列为空
- 清除白色的垃圾对象