Golang垃圾回收GC机制

238 阅读5分钟

标记清除法

Go1.3 之前标记清除mark and sweep

启动STW => mark标记 => sweep清除 => 停止STW

标记清除原理步骤

1, 在触发GC操作时,先执行stop the word(STW),暂停程序逻辑,找出可达和不可达对象

2, 进行可达标记,没有被标记的对象表示当前程序不需要,被定义为垃圾,进行回收

3, 回收后STW结束,程序恢复运行,循环重复这个过程,直到程序结束

标记清除缺点

  • STW会让程序暂停导致程序卡顿
  • 标记需要扫描整个heap
  • 清除数据会产生heap碎片

三色标记法

Go1.5 三色标记

三色标记法原理步骤

使用白色、灰色、黑色三个标记表

1,程序起初创建时,使用的对象全部标记为白色,放入白色标记表中

2,将程序的根节点集合展开,遍历root set(非递归形式,深度为1,即bfs广搜),得到灰色节点

3,遍历灰色标记表,将可达对象从白色标记为灰色(深度为1),遍历结束后,原本的灰色标记为黑色

4,重复上一步,直到灰色标记表中没有任何对象

5,将白色标记表中的节点进行回收操作

image-20240413093027860.png

1460000020086772.gif

三色标记法分析

如果三色标记法不启用STW,在扫描A节点时将B和C两个节点标记为灰色,并将A节点标记为黑色;然后开始扫描D节点,此时另一个goroutine修改了D对E的引用,变成了A对E进行引用,如下图,这时E节点最终还是白色,会被回收。

image-20240413094515001.png

因此推出三色标记法正常运作不能发生的两个条件,(只要一个条件不满足就可以正常运作)

  • 条件1,一个白色对象被黑色对象引动(白色对象被挂载黑色下)
  • 条件2,灰色同时丢失此白色对象

为了打破这两个条件,引出三色标记法的强、弱三色不变式

  • 强三色不变式:强制性的不允许黑色对象引用白色对象 => 破坏条件1
  • 弱三色不变式:黑色可以引用白色,但是白色必须存在其他灰色对象对它的引用,或可达它的链路上游存在灰色对象 => 破坏条件2

插入/删除写屏障机制

为了实现三色不变式,引出屏障机制

  • 插入写屏障 => 对象被引用时触发的机制

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

效果:满足强三色不变式(不存在黑色对象引用白色,因为白色被强制标记为灰色)

注意:插入写屏障只针对于堆空间,栈上不触发。栈上依然使用STW重新扫描

插入屏障不足:结束时需要STW重新扫描栈空间

  • 删除写屏障 => 对象被删除引用时触发的机制

具体操作:被删除的对象的直接下游对象如果自身为灰色或白色,那么其这个下游对象就被标记为灰色

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

待解决:栈上好像会触发删除写屏障,而在混合写屏障机制中栈上不会触发任何写屏障机制

删除屏障不足:回收精度低、一个对象即使被删除了最后一个指向它的指针也依旧可以活过这一轮,在下一轮被删除

混合写屏障

Go1.8 三色标记法+混合写屏障

混合写屏障满足了弱三色不变式

混合写屏障原理步骤

1,GC开始将栈上的可达对象全部扫描并标记为黑色(之后不在进行第二次重复扫描,无需STW)

2,GC期间,任何栈上创建的对象均为黑色,(栈上的对象指的是指针在栈上,指向的对象本身在堆中)

5,栈上不启动任何屏障机制;堆上启用屏障

3,堆上被删除的对象被标记为灰色(删除写屏障)

4,堆上被添加的对象被标记为灰色(插入写屏障)

混合写屏障场景

场景1:对象被一个堆对象删除引用,成为栈对象的下游
// 前提:堆对象4->next = 对象7;// 对象7 被 对象4 引用
栈对象1->next = 对象7;// 将堆对象7挂载在 栈对象 下游
堆对象4->next = null;//对象4 删除引用 对象7 

对象4删除了对象7的引用关系,因为对象4是堆区,所以触发删除屏障,标记被删除的对象7为灰。

场景2:对象被一个栈对象删除引用,成为另一个新建栈对象的下游
//前提:栈对象2->next = 对象3
new 栈对象9    
栈对象9—>next = 对象3
栈对象2->next = null

栈上创建对象均标记为黑色,此时栈对象9为黑色

对象2删除对象3时直接删除,因为栈不启动写屏障

场景3:对象被一个堆对象删除引用,成为另一个堆对象的下游
//前提:堆对象4->next = 对象7
堆对象10 -> next = 对象7; // 将堆对象7 挂在 堆对象10 下游
堆对象4 -> next = null; // 对象4 删除引用 对象7

堆对象10添加下游引用对象7,触发插入写屏障机制,被添加的对象7标记为灰色,对象7变成灰色

堆对象删除下游引用堆对象7,触发删除写屏障机制,被删除的对象7标记为灰色,对象7被标记为灰色。

场景4:对象从一个栈对象删除引用,成为另一个堆对象的下游
//前提: 栈对象1->next = 栈对象2
//前提: 堆对象4->next = 对象7
​
栈对象1 -> next = null; // 对象1 删除引用 对象2
堆对象4 -> next = 栈对象2 // 对象4 添加栈对象2

栈对象1删除对堆对象2的引用。(栈空间不触发写屏障)

对象4在删除的时候,触发写屏障,标记被删除对象7为灰色。保护对象7及其下游节点。

参考链接

https://idiotsky.top/2017/08/16/gc-three-color/

https://www.bilibili.com/video/BV1wz4y1y7Kd