GC之三色算法

1,240 阅读3分钟

前言

判断对象存活主要有两种方式,一种是引用计数方式,另外一种就是可达性分析。go 中判断对象存活采用的是可达性分析,而其 gc 使用的便是三色标记算法。

gc的核心目标

gc的核心目标是系统能够长期稳定地运行,阻碍这一目的的核心限制之一便是机器内存的限制,机器内存不是无尽的,而是有限的,所以需要对程序所申请的内存进行管理。目前通用的内存管理方式有三种:

  1. 开发者手动分配和回收,类似 c/c++
  2. 由系统自动分配和回收,类似java/go
  3. 无 GC,编译时决定对象生命周期,类似 rust 1与2是运行时决定对象的生命周期,3 是编译时就已经决定对象的生命周期,具体参见rust 的所有权

系统长期稳定运行的重要指标是,吞吐和延时。

  1. 吞吐关注的是单位时间内系统能够处理的任务量。
  2. 延时关心的是每个任务的响应时间。

我们的目的都是高吞吐和低延时,但是这两个目标有时候是无法两全的。go 更加强调的是它的低延时,其 GC 的低延时也是其引以为豪的一大特性。

下面我们来看一下 Go 的GC 算法,三色标记清除算法。【ps.为什么是三色,而不是二色、四色,其实有点像数学上的四色定理】

三色标记清除算法的主要过程

前置条件,所有的节点都必须是三种颜色之一。

  1. 回收开始前所有的对象都是白色,例如现在有A、B、C D 4个对象,初始都是白色

图 1
2. 选出GC roots,也就是根节点对象,一般根节点对象的选择会选取栈中引用的对象和运行时的常量对象,这时候需要 STW(stop the word),所有运行中的程序都会到达 safe point 才会触发。將根节点对象标记为灰色。这里假设A是根节点对象,此时A 将会变成灰色,如图 2 所示

图 2

  1. 标记颜色,把A变为黑色,同时A的引用B变为灰色,若B有子引用对象,这一过程会持续下去,直至灰色节点为空

  2. 此时如果有新的对象生成,如图3节点 E所示,或者是白色对象的引用发生变更,如图3节点D所示

图 3

  1. 在下一轮遍历中,B、D、E节点都会被标记为黑色

图 4
6. 白色节点C被清除

图 5
7. 黑色节点与白色节点互换,不需要去移动内存对象

图 6

小结

官方给出的测试结果显示 go 的 stw 时间很短,毕竟其发生 stw 的阶段只会发生在标记 GC roots 的阶段以及标记完成阶段。但是这并不代表其一定比 jvm 的GC收集器优秀,毕竟jvm可以对吞吐和延时做取舍,选择更合适自己的 GC收集器。

参考资料

  1. V8 增量 GC 之三色标记
  2. 深入理解 java 虚拟机
  3. Wiki--Tracing_garbage_collection