0%

Golang GC垃圾回收机制

Golang 历代GC设计

  1. Go V1.3 之前使用标记清除 (mark & sweep)
  2. Go V1.5 使用三色标记法 + 读写屏障
  3. Go V1.8 使用 三色标记法 + 混合写屏障机制

标记清除

在程序运行过程中,会出现有些对象没能直接被程序引用到,GC检测到则会触发 STW(Stop The World)暂停程序业务逻辑。

图片

之后则会对可达的对象 1-2-3,4-7 做上标记。

图片

其余的对象 5,6 因为是不可达,则被GC回收。

图片

回收操作自此已经完成,STW暂停解除。程序将继续运行,然后循环重复这个过程,直到程序的生命周期结束。

标记清除的缺点

  1. STW 暂停会让程序出现卡顿 (影响最大)
  2. 需要扫描所有的节点,判断其是否可达
  3. 删除数据会导致产生碎片,对这些碎片的重用或重组比较繁琐

原 STW 流程
启动STW -> Mark标记 -> Sweep清除 -> 停止STW

为了缩短STW暂停程序的时间,把Sweep清除步骤放到了后面
启动STW -> Mark标记 -> 停止STW -> Sweep清除

三色标记法

一种优化之前Mark标记法的方法。

标记分为白色、灰色、黑色三种。

图片

初始状态所有的节点都是白色标记。

图片

从根节点开始往下遍历一层,给此时可达的节点打上灰色标记。

图片

接着遍历灰色节点的标记,给可达的节点打上灰色标记,而原来被遍历的节点则打上黑色标记。

图片

往后重复以上动作,直至没有灰色对象,收集所有白色对象,此时白色对象是垃圾。

如果在使用三色标记法不进行STW,可能会发生的事情:

  1. 一个白色的对象,被黑色对象引用(因为黑色对象不会再进行扫描,会造成误删这个被引用的白色对象)
  2. 灰色对象与它原本可达的的白色对象关系遭到破坏

以上两个条件都满足,则会出现对象丢失的现象

强弱三色不变式

如果三色标记中满足强/弱之一,则可以保证对象不会丢失

强三色不变式:强制性不允许黑色对象引用白色对象

图片

弱三色不变式:黑色对象可以引用白色对象,但是白色对象必须存在其它灰色对象对它进行引用,或者可达它的链路上游存在灰色对象

图片

屏障机制

屏障是一种额外的判断机制 (类似 Hook、回调、Handler 机制)

插入屏障

对象被引用的时候,触发的机制 (堆中启用)

具体操作:在 A 对象引用 B 时,B对象被标记为灰色。

满足:强三色不变式 (此时不存在黑色引用白色对象了,因为白色对象会变成黑色)

图片

在最终准备回收白色节点前,将会重新扫描一边栈空间。此时启动STW保护栈,防止外界干扰。最终在对栈内的节点进行一次三色标记法。

图片

插入写屏障的不足:结束时仍需要 STW 来重新扫描栈,大约需要10-100ms。

删除屏障

对象被取消引用的时候,出发的机制

具体操作:被删除的对象,如果自身为灰色或者白色,那么标记为灰色。

满足:弱三色不变式(保证灰色对象到白色对象的路径不会断)

图片

图片

当出现删除节点操作时,触发删除屏障,如果被删除的节点是灰色或者白色,该节点设置为灰色。

图片

最终扫描完之后只删除节点6.节点5,2,3留到下一轮删除。

图片

这是为了保证如果节点5万一被其它节点所引用,则延后删除。

删除写屏障的不足:回收精度低,一个对象即使删除了最后一个指向它的指针也依旧可以存活一轮,在下一轮 GC 中被清理。

混合写屏障机制

具体操作:

  1. GC 开始时将栈上的对象全部设置为黑色 (为了不再进行第二次重复扫描,无需 STW)
  2. GC 期间,任何在栈上创建的新对象,均为黑色
  3. 被删除的对象标记为灰色
  4. 被添加的对象标记为灰色

满足:变形的弱三色不变式(结合了插入、删除写屏障的优点)

图片

场景一

场景一:一个对象被一个堆对象删除引用时,此时成为栈对象的下游

图片

前提:堆对象4的下游对象是对象7(对象7被对象4引用)

下面两行分别是两个操作

  • 一个是把栈对象1的下游挂载上堆对象7
  • 一个是把堆对象4中对对象7的引用给删除

混合写屏障流程:

在开始操作时,先吧栈上的所有可达节点设置为黑色。

图片

此时删除对象4对对象7的引用,触发机制。因为对象4是在堆区,所以触发写屏障,标记被删除的对象7为灰色。

图片

场景二

场景二:对象被一个栈对象删除引用,成为另一个栈对象的下游

图片

具体场景:

图片

在程序运行过程中,一个新的栈对象9出现。

图片

此时栈对象9先引用了对象3,然后对象2取消对对象3的引用。(若对象9不引用对象3,对象2继续取消则会让对象3空过一轮,下一轮重复操作的时候作为垃圾去除)

场景三

场景三:对象被一个堆对象删除引用,成为另一个堆对象的下游

图片

具体场景,初始状态在堆处新增了一个节点,直接标记为黑色(此为特殊情况,用于展示机制)。

图片

对象10引用对象7,之后对象4取消对对象7的引用。在取消引用的时候触发了删除屏障,被删除的对象7被标记为灰色。

图片

场景四

场景四:对象从一个栈对象删除引用,成为另一个堆对象的下游

图片

具体场景,栈对象1删除对栈对象2的引用。

图片

堆的对象4把对对象7的引用转移到对象2。(删除对对象7的引用,添加对对象2的引用)删除对对象7的引用时,会触发删除屏障,把对象7标记为灰色

图片