**垃圾回收(Garbage Collection,简称GC)**是编程语言中提供的自动的内存管理机制,自动释放不需要的内存对象,让出存储器资源。GC过程中无需程序员手动执行。GC机制在现代很多编程语言都支持,GC能力的性能与优劣也是不同语言之间对比度指标之一。
1.标记清除(Go V1.3)
- 暂停程序,将程序中的对象分为可达和不可达
- 对可达对象进行标记,然后清除不可达对象
- 停止暂停,程序继续执行,直到程序结束
标记清除(mark and sweep)需要暂停程序(STW),此过程会浪费大量时间,使程序执行效率低下。同时在STW中,会对整个堆区(heap)进行扫描,清除未标记对象时也会产生一定的heap碎片。
在后期发展过程中将清除工作放在暂停STW之后,与程序同步进行,减少了STW的时间,但还是浪费了大量的时间。
2.三色标记法(Go V1.5)
-
存在STW的三色标记法
-
程序起初创建的对象都设为白色
-
GC开始时从根结点开始遍历所有对象,把遍历(非递归遍历)到的对象从白色标记为灰色
-
遍历灰色对象,将灰色对象引用的对象标记为灰色,同时将该灰色对象标记为黑色
-
重复上一步骤,直到没有灰色标记的对象
-
将剩余所有白色结点进行删除回收
-
此过程在开始之前就会加上STW,扫描完所有对象,并且标记完成后才会停止STW,这样还是会浪费大量时间。
-
没有STW的三色标记法
没有STW,即意味着在GC执行过程中,各个对象都可以进行读写操作,这样可能会导致被引用的对象被清除
条件1: 一个白色对象被黑色对象引用(白色被挂在黑色下)
条件2: 灰色对象与它之间的可达关系的白色对象遭到破坏(灰色同时丢了该白色)
如果当以上两个条件同时满足时,就会出现对象丢失现象!
为了提高GC效率,减少STW的时间,于是引入了一种屏障机制。
3.屏障机制
强-弱三色不变式
-
强三色不变式
强制不允许黑色对象引用白色对象
-
弱三色不变式
所有被黑色对象引用的白色对象都被灰色对象保护着
插入屏障
具体操作
满足
插入屏障只在堆区对象中使用,在栈中执行三色标记过程会启动STW
删除屏障
具体操作
满足
此方法中一个对象即使被删除了最后一个指向它的指针也依旧可以活过这一轮,在下一轮GC中被清理掉。回收精度较低。
混合写屏障(Go V1.8)
具体操作:
- GC开始时将栈上的对象全部扫描并标记为黑色(之后不再进行第二次重复扫描,无需STW),
- GC期间,任何在栈上创建的新对象,均为黑色。
- 堆中被删除的对象标记为灰色。
- 堆中被添加的对象标记为灰色。
满足: 变形的弱三色不变式.
屏障技术是不在栈上应用的,因为要保证栈的运行效率。
栈空间的特点是容量小,要求响应速度快,函数调用弹出需要频繁使用
使用场景
- 对象被一个堆对象删除引用,成为栈对象的下游
//前提:堆对象4->对象7 = 对象7; //对象7 被 对象4引用
栈对象1->对象7 = 堆对象7; //将堆对象7 挂在 栈对象1 下游
堆对象4->对象7 = null; //对象4 删除引用 对象7
new 栈对象9;
对象8->对象3 = 对象3; //将栈对象3 挂在 栈对象9 下游
对象2->对象3 = null; //对象2 删除引用 对象3
堆对象10->对象7 = 堆对象7; //将堆对象7 挂在 堆对象10 下游
堆对象4->对象7 = null; //对象4 删除引用 对象7
堆对象10->对象7 = 堆对象7; //将堆对象7 挂在 堆对象10 下游
堆对象4->对象7 = null; //对象4 删除引用 对象7
混合写屏障满足弱三色不变式,结合了删除写屏障和插入写屏障的优点,只需要在开始时并发扫描各个goroutine的栈,使其变黑并一直保持,这个过程不需要STW,而标记结束后,因为栈在扫描后始终是黑色的,也无需再进行re-scan操作了,减少了STW的时间。
总结:
-
GoV1.3- 普通标记清除法,整体过程需要启动STW,效率极低。
-
GoV1.5- 三色标记法, 堆空间启动写屏障,栈空间不启动,全部扫描之后,需要重新扫描一次栈(需要STW),效率普通
-
GoV1.8-三色标记法,混合写屏障机制, 栈空间不启动,堆空间启动。整个过程几乎不需要STW,效率较高。