Golang中的垃圾回收算法

Go GC的触发条件:

  • gcTriggerAlways: 强制触发GC
  • gcTriggerHeap: 当前分配的内存达到一定阈值时触发,这个阈值在每次GC过后都会根据堆内存的增长情况和CPU占用率来调整
  • gcTriggerTime: 当一定时间没有执行过GC就触发GC(2分钟)
  • gcTriggerCycle: runtime.GC()调用

 

Golang的GC算法是基于 标记-清除 算法,在此基础上做了改进

标记-清除算法主要进行了两个步骤:

  1. 标记:从程序的根节点开始, 递归地遍历所有对象,将能遍历到的对象打上标记
  2. 清除:回收所有未标记的的对象

mark and sweep算法在执行时,需要暂停程序,即stop the world!

标记-清除算法虽然简单,但是存在一些问题:

  1. STW,程序暂停,出现卡顿
  2. 标记需要扫面整个heap
  3. 清除数据会产生heap碎片

最重要的问题是算法会暂停整个程序。

Go语言采用的是三色并发标记法,使得在标记阶段和清除阶段都可以和应用逻辑的goroutine并发执行

首先说明一下三色标记法:

“三色”的概念可以简单理解为:

  • 黑色:该对象已标记,且其后代已处理
  • 灰色:该对象已标记,但其后代未处理
  • 白色:未被标记

步骤如下:

  • 初始化阶段,所有的对象都是白色
  • 从根对象遍历,扫描到可达对象后标记为灰色,加入灰色队列
  • 扫描灰色队列,将其置为黑色,其引用对象标记为灰色
  • 重复上一步,直到灰色队列为空,此时只存在黑色对象和白色对象,回收白色对象

Golang中的垃圾回收算法

Go的GC是并行GC,大部分是可以和用户代码同时运行的

主要分为四个阶段:

  1. Mark Setup:初始化GC任务,开启写屏障和辅助GC,这个过程需要STW
  2. Mark:从根对象扫描,进行标记,直到灰色队列为空,这个过程可以并行执行
  3. Mark Termination:完成标记,re-scan在检查一下,因为在Mark过程中,很有可能有新的对象分配或赋值,这些都会通过写屏障记录下来,这个过程也会STW。
  4. Sweep:根据标记结果,回收所有的白色对象

目前整个GC流程会进行两次STW(Stop The World), 第一次是Mark阶段的开始, 第二次是Mark Termination阶段

第一次STW会准备根对象的扫描, 启动写屏障(Write Barrier)和辅助GC(mutator assist).

第二次STW会重新扫描部分根对象, 禁用写屏障(Write Barrier)和辅助GC(mutator assist).

 

参考资料:

Golang源码探索(三) GC的实现原理

【译】 Golang 中的垃圾回收(一)

图解Golang的GC垃圾回收算法

常见GC算法及Golang GC

Golang垃圾回收 屏障技术