总结整理

一、v1.3 之前,标记-清除(Mark and Sweep)

  • 暂停业务逻辑,分类标记可达和不可达对象,然后清除不可达的对象

执行需要 STW,CPU 会全部进行 GC,致使程序卡顿

标记需要扫描整个 Heap,清除数据会产生 Heap 碎片

二、v1.3 简单优化

  • 将清除步骤移出 STW 范围,因为回收不可达对象不会出现写冲突

三、v1.5 三色并发标记

GC 和其他用户 goroutine 并发运行,但是需要一定时间的 STW(确定黑白对象之后才能放开 STW)

  • 每次新创建的对象,标记为白色
  • 从根节点开始遍历,遍历到的对象标记为灰色
    (只 BFS 遍历一次)
  • 遍历灰色集合,将灰色对象引用的对象标记为灰色,当前的灰色标记为黑色
  • 重复操作,直到灰色集合没有对象
  • 此时,白色对象就是不可达,需要被回收

当没有 STW 时,如果一个白色对象被黑色对象引用(白色被挂在黑色下)并且灰色对象与它之间的可达关系的白色对象遭到破坏 (灰色同时丢了该白色),就会出现对象丢失现象!

四、屏障机制

  1. 强三色不变式(不存在黑色引用白色的指针)

  2. 弱三色不变式(所有被黑色引用的白色都有灰色引用直接或间接保护)

插入屏障:在 A 引用 B 时,把 B 标记为灰色

  • 仅使用在堆空间对象的操作中,在栈空间对象操作不使用(需要速度较快,函数调用弹出频繁使用)
  • 但是由于栈空间不添加,要对栈空间重新三色标记扫描,短暂启动 STW

删除屏障:被删除的对象,如果是灰色或者白色,标记为灰色

  • 这种方式的回收精度低,一个对象即使被删除了最后一个指向它的指针也依旧可以活过这一轮,在下一轮GC中被清理掉

五、v1.8 混合写屏障

不在栈空间使用屏障

  • GC 开始将栈上的对象全部扫描,并将全部可达对象标记为黑色(之后不再进行第二次重复扫描,无需STW)
  • GC期间,任何在栈上创建的新对象,均为黑色
  • 被删除的对象标记为灰色
  • 被添加的对象标记为灰色

具体场景:

  • 对象被一个堆对象删除引用,成为另一个栈对象的下游
  • 对象被一个栈对象删除引用,成为另一个栈对象的下游
  • 对象被一个堆对象删除引用,成为另一个堆对象的下游
  • 对象从一个栈对象删除引用,成为另一个堆对象的下游

Reference

为什么go语言gc的时候要暂停整个程序?