Go语言中的GC
1. GC的简介
GC(Garbage Collection),垃圾回收,分配在堆上的内存不会再使用时,Go语言将会自动回收分配在堆上的内存,从而避免系统的内存被占满。Go的自动回收内存的特性,使得Go程序开发者可以更加专注于代码的效率,很大程度上避免了内存的泄漏。
Go语言中GC思想
Go中采用最简单的“标记-清除”,“标记有用的对象,清除无用的对象”,采用广度优先搜索算法,从根集合出发,进行可达性分析,标记有用对象。
2. 标记的开始:GC的root set (根集合)
根集合是GC在标记过程中最先检查的对象。主要包括:
- 全局变量:在程序编译期间就能确定,全局变量存在于程序的整个生命周期。
- 执行栈:Go语言中协程是分配在堆上,每个协程都含有自己的执行栈。
- 寄存器:寄存器的值可能表示一个指针,这些指针可能指向另一块内存空间。
3 串行GC(STW)
Go语言的老版本采用的是串行GC。首先根据可达性分析法,采用广度优先搜索算法,先从根集合出发寻找被应用的对象,对内存标记。通过下图可知,J内存块并未被标记,说明J内存块可以进行垃圾回收。
串行GC的**缺点:**需要stop the world (STW)暂停所有的业务,对性能的影响较大,难以支持高并发。
4.并发GC
为了减少GC对性能的影响,Go现在的版本支持并发进行垃圾回收。采用的标记法为三色标记法。
4.1 三色标记法
三色标记法:
- 黑色:表示有用的对象,并且已经分析扫描完
- 灰色:有用,但是未分析扫描完
- 白色:暂时无用
三色标记法的步骤:
- 第一,首先一开始所有的对象都是白色的,从根节点出发,将根节点可达的对象置为灰色。
- 第二,然后分析灰色节点指向的节点,将灰色节点指向的节点置为灰色节点,分析完一个灰色节点的所有指向后将灰色该灰色节点置为黑色,表示该节点有用,并且已经扫描完。
- 第三,重复步骤二,直到没有节点的颜色发生变化,剩下的白色节点就是无用的节点。
三色标记法主要用于解决并发GC问题,但是如果只有三色标记法不做其他处理的话,在进行GC的过程中如果有元素的删除,或者元素的插入,或者元素的指向关系发生变化的时候,可能会出现误清除对象的问题。解决办法:在进行增删改的时候加入屏障。
4.2 插入屏障
在并发标记中,一个已经置为黑色的对象A(表示已经扫描分析过了),这时候插入一个对象C,并将A对象指向C对象,在扫描完后C对象仍然是白色的,可能会出现误清除。在插入一个对象后(例如插入对象C),将该对象置成灰色,然后继续扫描,可以防止插入的新元素被误清除。
4.3 删除屏障
在进行并发标记的过程中,会存在某些对象被删除(记为C),被删除后的对象又被别的已经是黑色的对象进行连接。这时候,可能出现在全部扫描完后,C对象仍然是白色,会被误清除。加入删除屏障的时候可以避免该问题的发生,删除屏障即在进行删除操作的时候,将被删除的对象置为灰色,这样在后续的扫描中将,如果该对象后续有别的对象进行指向则不会被删除,若该对象不存在后续的指向,则将会被清除。
4.4 混合屏障
**Go的GC使用的是混合屏障,在进行插入和删除的操作的时候将对象置为灰色。**这样就可以实现高并发的垃圾回收,业务和GC可以同时进行。注意:GC还是会停止业务,在启动屏障的时候会暂停业务一小会。
5. GC的触发时机
Go语言中有三种GC的触发时机:
- 系统的定时触发:如果两分钟内没有触发GC,则会每隔两分钟进行触发一次GC。
- 用户显示调用:用户调用runtime.GC方法,进行强制触发
- 申请内存时触发:给对象申请堆空间的时候,可能会触发GC,调用mallocgc的方法。
6. GC的优化原则
从上述的GC的调用时机难以优化GC,GC的优化原则是优化代码的结构,减少在堆上产生垃圾。
减少在堆上产生垃圾的方法:
- 内存池化,内存池,例如channel中采用环形缓存,环形缓存可以一直复用,可以减少垃圾回收
- 减少逃逸:优化代码,尽可能在栈上分配内存。
- 使用空结构体:空结构体不占用内存空间。
7. GC的分析工具
GC的分析工具有:
- go tool pprof
- go tool trace
- go bulid -gcflags = “-m”
- GODEBUG=“gctrace=1”
- 最常用的是GODEBUG=“gctrace=1”,下图中展示的是采用GODEBUG=“gctrace=1分析GC的情况。
8. 总结
- Go语言可以自动回收堆上分配的内存,并且可以实现并发进行GC,性能较优
- Go语言采用的标记方法为三色标记法,并且为了进行并发GC,采用了混合屏障,有效的应对GC过程中的插入和删除操作。
- GC有三个触发的时机,分别是,定时触发、用户显示调用触发、申请内存时触发。
- GC的优化原则,尽量减少在堆上分配垃圾,内存池化、减少逃逸,使用空结构体。
- GC的分析中最常用的命令时GODEBUG=“gctrace=1