Golang入门——GC机制
- 常见GC算法
- 引用计数
- 标记清除
- 分代收集
- Go的GC
- mark内存标记
- 三色标记法
- STW(Stop The World)
- GC优化
- 写屏障
- 协助GC
- GC触发的时机
- 内存分配量到达阈值触发GC
- 定期触发GC
- 手动触发
- GC 性能优化
常见GC算法
引用计数
每个对象维护一个计数器,当引用该对象的对象被销毁时,计数器减一,当计数器为0时回收该对象。
代表语言:Python,PHP,Swift
优点:对象可以很快被回收,不会出现内存耗尽再回收的现象。
缺点:不能很好的处理循环引用,而且维护计数器需要开销。
标记清除
从根变量开始便利所有引用对象,当引用对象没有标记“被引用”,则回收该对象。
代表语言:GO(三色标记法)
优点:解决引用计数的缺点。
缺点:需要STW,暂时停止程序运行。
分代收集
按照对象的证明周期长短划分不同代空间,生命周期长的放入老年代,生命周期短的放入新生代,不同代有不用的回收算法以及回收频率。
代表语言:Java
优点:回收性能好。
缺点:算法复杂。
Go的GC
mark内存标记
在span的数据结构中维护位图allocBits表示内存块分配情况,同时还维护另一个位图gcmarkBits记录每块内存的标记情况。由于allocBits与gcmarkBits数据结构相同,回收时将allocBits指向gcmarkBits,表示只有标记过的内存才是存活的。gcmarkBits会在下次标记时重新分配内存。
三色标记法
灰色:对象还在标记队列中等待。
黑色:对象已被标记,gcmarkBits对应的位为1(该对象不会被本次GC清理)。
白色:对象未被标记,gcmarkBits对应的位为0(该对象会被本次GC清理)。
初始状态下所有对象都是白色。
例:
根对象引用了对象A、B、C,则A、B、C变为灰色对象。接下来分析对象A、B、C是否引用其他对象。如果只有C引用对象D,那么对象A、B、C变为黑色对象,对象D变为灰色,再分析对象D是否引用其他对象,如果有再进行类似以上操作,如果没有则仅将对象D变为黑色对象。
STW(Stop The World)
Go中的STW就是停止所有的goroutine,专心做垃圾回收,等待回收完毕再恢复goroutine。
GC优化
主要缩短STW时间,不断优化GC算法。
写屏障
写屏障的作用是使goroutine与GC同时运行的手段。能够大大缩短STW时间。
GC过程中新分配的内存不会被立即标记,用的正是写屏障技术,即GC过程中分配的内存不会在本次GC中清理。
协助GC
为了防止内存分配过快,在GC过程中如果goroutine需要分配内存,那么改goroutine会参与一部分GC工作。这种机制称为Mutator Assist。
GC触发的时机
内存分配量到达阈值触发GC
每次分配内存时检查当前内存分配量是否达到阈值,如果达到则启用GC。
阈值 = 上一次GC内存分配量 x 内存增长率
内存增长率由环境变量GOGC控制,默认为100。每当内存扩大一倍时启动GC。
定期触发GC
默认情况下,最长两分钟触发一次GC,在src/runtime/:forcegcperiod中定义
var forcegcperiod int64 = 2 * 60 * 1e9
- 1
手动触发
在程序中使用()手动触发GC,主要用于GC性能测试以及统计。
GC 性能优化
对象越多,GC性能越低。所以优化GC性能的思路之一为减少对象分配个数,比如对象复用或者大对象组合多个小对象等。