Golang中的垃圾回收算法
Go GC的触发条件:
- gcTriggerAlways: 强制触发GC
- gcTriggerHeap: 当前分配的内存达到一定阈值时触发,这个阈值在每次GC过后都会根据堆内存的增长情况和CPU占用率来调整
- gcTriggerTime: 当一定时间没有执行过GC就触发GC(2分钟)
- gcTriggerCycle: runtime.GC()调用
Golang的GC算法是基于 标记-清除 算法,在此基础上做了改进
标记-清除算法主要进行了两个步骤:
- 标记:从程序的根节点开始, 递归地遍历所有对象,将能遍历到的对象打上标记
- 清除:回收所有未标记的的对象
mark and sweep算法在执行时,需要暂停程序,即stop the world!
标记-清除算法虽然简单,但是存在一些问题:
- STW,程序暂停,出现卡顿
- 标记需要扫面整个heap
- 清除数据会产生heap碎片
最重要的问题是算法会暂停整个程序。
Go语言采用的是三色并发标记法,使得在标记阶段和清除阶段都可以和应用逻辑的goroutine并发执行
首先说明一下三色标记法:
“三色”的概念可以简单理解为:
- 黑色:该对象已标记,且其后代已处理
- 灰色:该对象已标记,但其后代未处理
- 白色:未被标记
步骤如下:
- 初始化阶段,所有的对象都是白色
- 从根对象遍历,扫描到可达对象后标记为灰色,加入灰色队列
- 扫描灰色队列,将其置为黑色,其引用对象标记为灰色
- 重复上一步,直到灰色队列为空,此时只存在黑色对象和白色对象,回收白色对象
Go的GC是并行GC,大部分是可以和用户代码同时运行的
主要分为四个阶段:
- Mark Setup:初始化GC任务,开启写屏障和辅助GC,这个过程需要STW
- Mark:从根对象扫描,进行标记,直到灰色队列为空,这个过程可以并行执行
- Mark Termination:完成标记,re-scan在检查一下,因为在Mark过程中,很有可能有新的对象分配或赋值,这些都会通过写屏障记录下来,这个过程也会STW。
- Sweep:根据标记结果,回收所有的白色对象
目前整个GC流程会进行两次STW(Stop The World), 第一次是Mark阶段的开始, 第二次是Mark Termination阶段
第一次STW会准备根对象的扫描, 启动写屏障(Write Barrier)和辅助GC(mutator assist).
第二次STW会重新扫描部分根对象, 禁用写屏障(Write Barrier)和辅助GC(mutator assist).
参考资料: