此篇主要记录一下常见的GC算法以及Go语言采用的垃圾回收方式。

常见的GC算法

1. 引用计数(reference counting)

引用计数算法无法处理循环引用的问题。

2. 标记-清除(mark and sweep)

从根变量开始迭代所有被引用的对象,进行标记,最后对未被标记的变量进行清除。此算法会造成STW。为了优化这个问题,产生了三色标记算法:


1. 起初所有对象都是白色。
2. 从根出发扫描所有可达对象,标记为灰色,放入待处理队列。
3. 从队列取出灰色对象,将其引用对象标记为灰色放入队列,自身标记为黑色。
4. 重复步骤 3,直到灰色对象队列为空。此时白色对象即为垃圾,进行回收。

三色标记的一个明显好处是能够让用户程序和 mark 并发的进行。

3. 复制收集(Copy and Collection)

扫描时并不是对引用对象进行标记,而是开辟一块新的内存空间,将对象复制到新的空间中。一次扫描结束之后,所有存在于新空间的对象就是所有的非垃圾对象。

4. 分代收集(generation)

  • 根据对象的存活周期不同将内存划分为新生代和老年代,存活周期短的为新生代,存活周期长的为老年代。这样就可以根据每块内存的特点采用最适当的收集算法。
  • 对新生代进行高频小回收,然后将遗留下来的对象归为老年代,对所有对象进行低频大回收。
  • 大多数分代回收算法都采用的复制收集方法,因为小回收中垃圾的比例较大。
  • 这种方式存在一个问题:如果在某个新生代的对象中,存在老年代的对象对它的引用,它就不是垃圾了。那如何制止小回收对其回收呢?这里用到了一种叫做 写屏障(Write Barrier) 的方式。写屏障不仅用于分代收集,也用于其他GC算法中。在此算法的表现是,用一个记录集来记录从新生代到老年代的引用。

Go语言的垃圾回收

Go语言垃圾回收总体采用的是经典的mark and sweep算法。据官方说法,Go GC的基本特征是“非分代、非紧缩、写屏障、并发标记清理”。

Golang GC算法的里程碑

  • v1.1 STW
  • v1.3 Mark STW, Sweep 并行
  • v1.5 三色标记法
  • v1.8 hybrid write barrier

Go是一种使用了写屏障的并发标记清除的垃圾回收方式

GODEBUG=gctrace=1 go run main.go

如果在任何go run命令前面加上GODEBUG=gctrace=1,go就会打印关于垃圾回收操作的一些分析数据。