如果❤️我的文章有帮助,欢迎点赞、关注。这是对我继续技术创作最大的鼓励。[更多往期文章在我的个人博客] coderdao.github.io/
什么是垃圾回收(GC)
垃圾回收算法 中比较常见的有 标记清除(Mark-Sweep) 和 引用计数(Reference Count),而Golang 采用 标记清除法。并在 标记清除法 上使用 三色标记法 和 写屏障 等技术大大提高工作效率。
标记清除收集器是 跟踪式 垃圾收集器,它的工作流程大致分为两个阶段:标记(Mark) 和 清除(Sweep):
-
标记阶段 — 从
根对象(root)开始查找并标记堆中所有存活对象 -
清除阶段 — 回收
堆中未被标记的垃圾对象 并 将回收的内存加入空闲链表
什么是三色标记法

三色标记算法将程序中的对象分成白色、黑色、灰色三类。
-
白色:不确定对象
-
灰色:存活对象,子对象待处理
-
黑色:存活对象
三色法标记过程
- 标记开始:所有对象加入白色集合(这一步需 STW)。

- 将
根对象标记为灰色,加入灰色集合

- 垃圾搜集器取出一个灰色对象,将其标记为黑色,并将其
指向对象标记为灰色,加入灰色集合

- 重复
第三步过程,直到灰色集合为空为止

- 标记阶段结束:那么白色对象为
需要清理对象;而黑色对象为根可达对象,不能被清理(这一步需 STW)

并发时三色法存在的问题
由于 三色标记法 多了一个 白色状态 来存放 不确定对象。
结合并发执行的后续标记阶段,就有可能会造成一些遗漏:比如早先被标记为黑色对象 可能目前已经变不可达。
三色标记法 并发情况下执行 仍存在一个问题,即在 GC 过程中,对象指针发生了改变。
比如下面的例子:A (黑) -> B (灰) -> C (白) -> D (白)
正常情况下:D 对象最终会被标记为黑色,不应被回收。
但在 标记 和用户程序 并发执行:用户程序删除了 C 对 D 的引用,而 A 获得了 D 的引用。
标记继续进行,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, 并发)

随时随地看视频