Go1.3之前采用标记清除法,Go1.3之后采用三色标记法。Go1.8之后采用采用三色标记法+混合写屏障。 Go垃圾回收器是并发的,而且在程序运行过程中可以让GC和应用程序一起工作,从而最大限度地减少对应用的影响。
三色标记法:
将对象分为三种颜色:
- 白色:尚未检查的对象,假设最初所有对象都是白色。
- 灰色:已经发现但是其引用地对象尚未检查地对象。
- 黑色:当前已检查并且其引用的对象也确认存活地对象。
过程:
- 初始化,将所有对象设为白色,将根集合(全局对象、栈上的对象等)标记为灰色。
- 标记:从灰色对象开始遍历,将所有被引用的白色对象变成灰色,遍历结束后把当前对象变黑。
- 扫描:不断重复以上过程,直到没有灰色对象。最终白色的对象即为未被引用地对象,可以回收。
三色不等式:
- 强三色不等式:不存在黑色对象引用到白色对象的指针。
- 弱三色不等式:所有被黑色对象引用的白色对象都处于灰色保护状态。
如果对象的引用被用户修改了,那么之前的标记就无效了,因此采用了写屏障技术,当对象新增或者更新会将其着色为灰色,确保这些改动不会导致存活的对象被错误地回收。
由于插入写屏障和删除写屏障在结束时需要STW来重新扫描,带来性能瓶颈,因此引入混合写屏障,分为四部:
- GC开始将栈上地对象全部扫描并标记为黑色(之后不再进行第二次重复扫描,无需STW)
- GC期间,任何在栈上创建的新对象,均为黑色。
- 被删除引用的对象标记为灰色
- 被添加引用的对象标记为灰色。
有界延迟
Go垃圾回收器设计目标之一是保证低延迟,以避免垃圾回收对应用程序性能造成显著地影响。因此Go语言设计了多种策略来限制每次垃圾回收所花费的时间,开发人员可以通过控制GOGC,或者debug.SetGCPercent()等方法来人工干预GC过程。
Go语言的GC流程
- 标记阶段:标记所有存活的对象。
- 清楚阶段:清楚那些未被标记的对象。
- 压缩阶段:调整堆中的对象以减少内存碎片。 主要是通过合理设置GC的策略与参数,来降低垃圾回收对程序性能的影响,使得内存管理变得更加简单。
调优是一个反复迭代的过程,持续监控和分析是关键。
GC调优,主要是两方面:一方面减少用户代码分配内存的数量(即对程序的代码行为进行调优),另一方面最小化Go的GC对CPU的使用率(即调整 GOGC)。
GO 进行GC调优有以下几个主要方式:
- 配置 GOGC,控制触发 GC 的内存增长百分比,降低GC的频率。
- 优化内存分配和释放,减少go对象分配和释放的频率。控制内存分配的速度,比如限制Goroutine的数量)
- 调整数据结构和算法,减少并复用内存,比如减少指针的使用,再比如少量使用+连接string,避免重复扩容,再比如使用slice时提前分配足够的内存来降低扩容带来的拷贝。
- 更新GO版本,利用最新的性能改进和垃圾回收优化。