Golang 垃圾回收(GC)原理
如果❤️我的文章有帮助,欢迎点赞、关注。这是对我继续技术创作最大的鼓励。更多往期文章在我的个人博客
什么是垃圾回收(GC)
垃圾回收算法
中比较常见的有 标记清除(Mark-Sweep)
和 引用计数(Reference Count)
,而Golang 采用 标记清除法
。并在 标记清除法
上使用 三色标记法
和 写屏障
等技术大大提高工作效率。
标记清除
收集器是 跟踪式
垃圾收集器,它的工作流程大致分为两个阶段:标记(Mark)
和 清除(Sweep)
:
- 标记阶段 — 从
根对象(root)
开始查找并标记堆
中所有存活对象
- 清除阶段 — 回收
堆中未被标记
的垃圾对象 并 将回收的内存加入空闲链表
什么是三色标记法
三色标记算法将程序中的对象分成白色
、黑色
、灰色
三类。
- 白色:不确定对象
- 灰色:存活对象,子对象待处理
- 黑色:存活对象
三色法标记过程
-
全部对象加入白色集合(
STW
)。 -
将
根对象
标记为灰色
加入灰色集合
-
垃圾收集器在
灰色集合
逐个取出对象标记为黑色
。而它的指向对象
标记为灰色
也加入灰色集合
-
重复
第三步过程
,直到灰色集合为空为止 -
以上4步结束后:需要
清理白色对象
;保留黑色对象(根可达对象
)(STW
)
并发时三色法存在的问题
由于 三色标记法
多了一个 白色状态
来存放 不确定对象
。
结合并发执行
的后续标记阶段,就有可能会造成一些遗漏:比如早先被标记为黑色对象
可能目前已经变不可达
。
但 并发情况下执行
依然存在问题:GC 过程中对象、指针被改变。
比如下面的例子:A (黑) -> B (灰) -> C (白) -> D (白)
通常 D 对象最后加入黑色集合不被回收。
但在并发情况下:A 获得了 D 的引用, 而C 对 D 的引用被用户删除 。
这是 GC并发运行,D 就在也没机会被标记为黑色(A 已经处理,这一轮不会再处理)
A (黑) -> B (灰) -> C (白)
↓
D (白)
这显然是不允许的。
什么是写屏障
为了解决这个问题,Golang 的垃圾收集器使用了写屏障(Write Barrier)
技术:
当对象
新增
、更新
时,将对象着色为灰色
。
写屏障主要做一件事情:修改原来写逻辑,在对象新增
、更新
同时给它着色为灰色。
在并发情况下写屏障
保证三色标记法
的正确运行。
如此一来:即使 GC与用户程序并发执行。对象引用发生变化,垃圾收集器也能正确处理。
而写屏障标记成灰色的对象会在 在新的 GC 过程中所有已存对象 又从白色开始
逐步 按上面 三色法标记过程
分类处理
完整的 GC 分为四个阶段:
1)标记准备(Mark Setup,需 STW),打开写屏障(Write Barrier) 2)使用三色标记法标记(Marking, 并发) 3)标记结束(Mark Termination,需 STW),关闭写屏障。 4)清理(Sweeping, 并发)