手记

Golang 垃圾回收(GC)原理

如果❤️我的文章有帮助,欢迎点赞、关注。这是对我继续技术创作最大的鼓励。[更多往期文章在我的个人博客] coderdao.github.io/

什么是垃圾回收(GC)

垃圾回收算法 中比较常见的有 标记清除(Mark-Sweep)引用计数(Reference Count),而Golang 采用 标记清除法。并在 标记清除法 上使用 三色标记法写屏障 等技术大大提高工作效率。

标记清除收集器是 跟踪式 垃圾收集器,它的工作流程大致分为两个阶段:标记(Mark)清除(Sweep)

  • 标记阶段 — 从 根对象(root) 开始查找并标记 所有存活对象

  • 清除阶段 — 回收 堆中未被标记 的垃圾对象 并 将 回收的内存加入空闲链表

什么是三色标记法

三色标记算法将程序中的对象分成白色黑色灰色三类。

  • 白色:不确定对象

  • 灰色:存活对象,子对象待处理

  • 黑色:存活对象

三色法标记过程

  1. 标记开始:所有对象加入白色集合(这一步需 STW)。

  1. 根对象 标记为 灰色,加入 灰色集合

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

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

  1. 标记阶段结束:那么白色对象为需要清理对象;而黑色对象为根可达对象,不能被清理(这一步需 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, 并发)

2人推荐
随时随地看视频
慕课网APP