抱歉,您的瀏覽器無法訪問本站
本頁面需要瀏覽器支持(啟用)JavaScript
了解詳情 >

前言

Go跟Java一样都是自动垃圾回收、简称GC,即在后台运行一个守护线程,监控各个对象的状态,识别并且释放不在使用的对象的内存空间。

目前Go使用的垃圾回收策略是三色标记法+混合写屏障,下面就来学习一下GC的细节把。

标记清除法

在Golang的1.3版本使用的就是标记清除法(mark and sweep)。

  1. 进行STW(stop the world)即暂停程序运行
  2. 开始标记,程序将可到达内存进行标记
  3. 标记结束,并清除未标记的内存
  4. 恢复程序运行

为了提升程序性能,后续变成先恢复程序运行,在清除未标记的内存。

三色标记法

为了提高程序性能,更好避免STW所带来的GC停顿。Go1.5版本引入了三色标记法,即将对象分为白色、灰色、黑色三种标识。具体流程如下

  1. 初始化时,将所有对象标记为白色
  2. 开始GC,遍历根对象,将直接可到达的对象标记为灰色
  3. 遍历灰色对象,将直接可到达的对象标记为灰色,并将自身标记为黑色
  4. 不断重复第3步骤,直到灰色节点为空时。
  5. 回收所有白色对象

但是当GC和程序一起运行时,可能会产生对象丢失的问题。

例如下面的情况:

  1. 一个被标记为白色的对象被黑色对象所引用
  2. 引用该白色对象的灰色对象断开联系关系

因此引入了两个概念:强三色不变式和弱三色不变式

  • 强三色不变式:即黑色对象不能引用白色对象。这样就避免扫描不到的问题,从而导致被错误回收。
  • 弱三色不变式:即黑色对象可以引用白色对象,但白色对象需要被灰色对象间接或直接所引用。这样就避免白色对象一定会被扫描到,从而避免被错误回收。

因此为了实现这两种原则,就提出了:插入写屏障和删除写屏障

插入写屏障

插入写屏障实现了强三色不变式原则,即白色对象被黑色对象所引用的时候,会将白色对象标记为灰色。需要注意的是插入写屏障只针对堆内存对象,这是因为栈本身的特点就是空间小、速度快,因此不适用于屏障。

因此在栈空间可能还是会存在白色对象被引用的情况,因此在进行三色扫描之后,还会对栈空间进行STW暂停程序运行,重新进行一次三色扫描(10ms-100ms)

删除写屏障

删除写屏障实现的是弱三色不变式原则,则被删除的对象为白色或者或者灰色时,会将自身标记为灰色,防止被回收。但这会导致回收的精度降低,即使没有对象引用它,他仍然会存留到下一次GC。

在Golang1.5版本中GC使用的就是三色标记法+插入写屏障来实现的

混合写屏障

在Golang 1.8的版本中引用了混合写屏障,满足弱三色扫描不变式的原则,同时避免对栈空间的STW和重新三色扫描,提升了GC效率。

混合写屏障规则如下:

  1. GC开始时将全部能扫描到的栈上对象标记为黑色
  2. GC期间,栈空间创建的所有新对象都为黑色
  3. 堆空间被删除的对象被标记为灰色
  4. 堆空间新增的对象被标记为灰色

小结

  • 即Go1.3版本包括之前的版本使用的是标记-清除的方式,会导致较长实践STW
  • Go1.5版本使用三色标记法+插入写屏障来提升GC效率,但栈空间仍然需要进行STW来避免对象被错误回收
  • Go1.8版本之后使用三色标记法+混合写屏障来提升GC效率,整个过程基本不需要GC,大大提升了效率