Go垃圾回收GC详解

Go的三色标记GC

  • 引用计数:对每个对象维护一个引用计数,当引用该对象的对象被销毁时,引用计数减1,当引用计数器为0是回收该对象 。
  • 优点:对象可以很快的被回收,不会出现内存耗尽或达到某个阀值时才回收 。
  • 缺点:不能很好的处理循环引用,而且实时维护引用计数,有也一定的代价 。
  • 代表语言:Python、php、Swift
  • 标记-清除:从根变量开始遍历所有引用的对象,引用的对象标记为"被引用",没有被标记的进行回收 。
  • 优点:解决了引用计数的缺点 。
  • 缺点:需要STW,即要暂时停掉程序运行 。
  • 代表语言:Golang(其采用三色标记法)
  • 分代收集:按照对象生命周期长短划分不同的代空间,生命周期长的放入老年代,而短的放入新生代,不同代有不能的回收算法和回收频率 。
  • 优点:回收性能好
  • 缺点:算法复杂
  • 代表语言: JAVA,python
root
首先标记root根对象,根对象的子对象也是存活的 。
根对象包括:全局变量,各个G stack上的变量等 。
标记
【Go垃圾回收GC详解】在之前的Go语言——内存管理一文中,分析过span是内存管理的最小单位,所以猜测gc的粒度也是span 。
type mspan struct {
// allocBits and gcmarkBits hold pointers to a span's mark and
// allocation bits. The pointers are 8 byte aligned.
// There are three arenas where this data is held.
// free: Dirty arenas that are no longer accessed
// and can be reused.
// next: Holds information to be used in the next GC cycle.
// current: Information being used during this GC cycle.
// previous: Information being used during the last GC cycle.
// A new GC cycle starts with the call to finishsweep_m.
// finishsweep_m moves the previous arena to the free arena,
// the current arena to the previous arena, and
// the next arena to the current arena.
// The next arena is populated as the spans request
// memory to hold gcmarkBits for the next GC cycle as well
// as allocBits for newly allocated spans.
//
// The pointer arithmetic is done "by hand" instead of using
// arrays to avoid bounds checks along critical performance
// paths.
// The sweep will free the old allocBits and set allocBits to the
// gcmarkBits. The gcmarkBits are replaced with a fresh zeroed
// out memory.
allocBits *gcBits
gcmarkBits *gcBits
}

Go垃圾回收GC详解

文章插图
bitmap
如图所示,通过gcmarkBits位图标记span的块是否被引用 。对应内存分配中的bitmap区 。
三色标记
  • 灰色:对象已被标记,但这个对象包含的子对象未标记
  • 黑色:对象已被标记,且这个对象包含的子对象也已标记,gcmarkBits对应的位为1(该对象不会在本次GC中被清理)
  • 白色:对象未被标记,gcmarkBits对应的位为0(该对象将会在本次GC中被清理)
例如,当前内存中有A~F一共6个对象,根对象a,b本身为栈上分配的局部变量,根对象a、b分别引用了对象A、B, 而B对象又引用了对象D,则GC开始前各对象的状态如下图所示:
  1. 初始状态下所有对象都是白色的 。
  2. 接着开始扫描根对象a、b; 由于根对象引用了对象A、B,那么A、B变为灰色对象,接下来就开始分析灰色对象,分析A时,A没有引用其他对象很快就转入黑色,B引用了D,则B转入黑色的同时还需要将D转为灰色,进行接下来的分析 。
  3. 灰色对象只有D,由于D没有引用其他对象,所以D转入黑色 。标记过程结束
  4. 最终,黑色的对象会被保留下来,白色对象会被回收掉 。

Go垃圾回收GC详解

文章插图
STW
stop the world是gc的最大性能问题,对于gc而言,需要停止所有的内存变化,即停止所有的goroutine,等待gc结束之后才恢复 。
触发
  • 阈值:默认内存扩大一倍,启动gc
  • 定期:默认2min触发一次gc,src/runtime/proc.go:forcegcperiod
  • 手动:runtime.gc()
go gc
Go垃圾回收GC详解

文章插图
go gc
GO的GC是并行GC, 也就是GC的大部分处理和普通的go代码是同时运行的, 这让GO的GC流程比较复杂.
  1. Stack scan:Collect pointers from globals and goroutine stacks 。收集根对象(全局变量,和G stack),开启写屏障 。全局变量、开启写屏障需要STW,G stack只需要停止该G就好,时间比较少 。


    推荐阅读