垃圾回收机制

Garbage collection

Posted by yueLng on 2018-02-22

In computer science, garbage collection (GC) is a form of automatic memory management. The garbage collector, or just collector, attempts to reclaim garbage, or memory occupied by objects) that are no longer in use by the program. Garbage collection was invented by John McCarthy) around 1959 to simplify manual memory management in Lisp).[1]#cite_note-1)[2]#cite_note-2)

引用计数(Reference-Counting-Collector)

引用计数的原理比较简单,为每一个要管理的对象,记录一个被引用的计数,当被引用,计数加一,当不引用,计数减一,当计数为零时,则回收该对象。一般使用哈希表作为管理数据结构,键为管理对象的地址,其计数为值。

引用计数的一个缺点在于,需要频繁更新引用计数,另一缺点是引用形成环路造成循环引用,导致计数永远不会零,对象永不会被回收

追踪式(Trace-Collector)

跟踪式就是将所有被管理的对象以引用关系组织成一张有向图,然后从必定不需回收的节点作为根节点(root)出发,trace其引用关系,遍历所有可达节点。只要节点可达就不需回收,如果不可达就要回收。

跟踪式为自动管理,有以下几种算法:

  1. Mark-Sweep法(标记清除法)
    这个算法分为两步,标记和清除。
  • 标记:从程序的根节点开始, 递归地 遍历所有对象,将能遍历到的对象打上标记。
  • 清除:讲所有未标记的的对象当作垃圾销毁。

但是这个算法也有一个缺陷,就是人们常常说的 STW 问题(Stop The World)。因为算法在标记时必须暂停整个程序,否则其他线程的代码可能会改变对象状态,从而可能把不应该回收的对象当做垃圾收集掉。另外一个问题在于递归遍历整个对象树,当程序中对象逐渐增多,导致gc时间过长。

  1. Mark-Compact
    类似标记-清除算法,标记-压缩同样需要标记出所有被引用节点。但是下一步并不是光简单的清除未被引用的节点,还需要将被引用的节点压缩到内存上的一片连续区域,来优化外部碎片问题。

  2. 三色标记法
    三色标记法是传统 Mark-Sweep 的一个改进,它是一个并发的 GC 算法,三色标记的一个明显好处是能够让用户程序和 mark 并发的进行,具体可以参考论文:《On-the-fly garbage collection: an exercise in cooperation.》。

黑色:根对象,或者该对象与它的子对象都被扫描
灰色:对象本身被扫描,但还没扫描完该对象中的子对象
白色:未被扫描对象,扫描完成所有对象之后,最终为白色的为不可达对象,即垃圾对象

首先创建三个集合:白、灰、黑。
将所有对象放入白色集合中。
然后从根节点开始遍历所有对象(注意这里并不递归遍历),把遍历到的对象从白色集合放入灰色集合。
之后遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色集合,之后将此灰色对象放入黑色集合
重复上步直到灰色中无任何对象
通过write-barrier检测对象有变化,重复以上操作
收集所有白色对象(垃圾)

  1. Generational
    这种算法按被管理对象的生命周期长短使用不同的算法对其进行管理。如果几次GC触发回收仍都不需回收的对象被认为是常驻内存的对象,被称作老生代对象(old generation),会被放入老生代区域,使用mark&compact算法对其进行管理。而刚被分配的对象,被称作年轻代对象(young generation),会被放入年轻代区域,使用copy算法对其进行管理。

所以记住任何时候都应该想办法确保你的对象要么分配在栈上,要么尽可能把多个对象合并让它们重复使用一块内存

Golang GC

  1. 何时触发GC
    在堆上分配大于 32K byte 对象的时候进行检测此时是否满足垃圾回收条件,如果满足则进行垃圾回收。还有一种是主动垃圾回收,通过调用 runtime.GC(),这是阻塞式的

参考内容

  1. 计算机体系 – 垃圾收集器
  2. golang 垃圾回收机制
  3. Golang 垃圾回收剖析
  4. Garbage_collection wiki)