Go垃圾回收方法

go使用的垃圾回收机制是三色标记法配合写屏障和辅助GC,三色标记法是标记-清除法的增加版本

标记-清除法(mark and sweep)

  • 标记。先STW(stop the world),暂停整个程序的全部运行线程,将被引用的对象打上标记
  • 清除没有被打标记的对象,即回收内存资源,然后恢复运行线程。

这种方案有个很大的问题就是:通过STW保证GC期间标记对象的状态不能变化,整个程序都要暂停掉,在外部看到程序就会卡顿。

三色标记法

  • 初始状态都为白色
  • 从root根出发扫描所有根对象,将他们引用的对象标记为灰色。root区域主要是程序运行到当前时刻的栈和全局数据区域
  • 分析灰色对象是否引用了其他对象,如果没有引用其他对象则将该灰色对象标记为黑色;如果有引用则将它变为黑色的同时,将它引用的对象也变为灰色
  • 重复上面第三步,直到灰色对象队列为空,此时白色对象即为垃圾,进行回收

Go GC如何工作

  • Mark包含两部分
    • Mark Prepare:初始化GC任务,包括开启写屏障(write barrier)和辅助GC(mutator assist),统计root对象的任务数量,这个过程需要STW
    • GC Drains:扫码所有root对象,包括全局指针和goroutine(G)栈上的指针(扫码对应G栈时需停止该G),将其加入标记队列(灰色队列),并循环灰色队列的对象,直到灰色队列为空,该改成后台并行执行。
  • Mark Termination:完成标记工作,重新扫描(re-scan)全局指针和栈。因为Mark和用户程序是并行的,所以在Mark过程中可能会有新的对象分配和指针赋值,这个时候需要通过写屏障(write barrier)记录下来,re-scan再检查一次,这个过程会STW
  • Sweep:按照标记结果回收所有的白色对象,该过程后台并行执行
  • Sweep Termination:对未清扫的span进行清扫,只有上一轮GC的清扫工作完成后才可以开始新一轮的GC,如果标记期间用户逻辑改变了刚打完标记的对象的引用状态,会怎么办呢?

写屏障(write Barrier)

每一轮GC开始时会初始化一个叫做“屏障”的东西,然后由它记录第一次scan时各个对象的状态,以便和第二次re-scan进行对比,引用状态变化的对象被标记为灰色以防止丢失,将屏障前后状态未变化的对象继续处理

辅助GC

如果GC回收速度跟不上用户代码分配对象的速度,Go语言如果发现回收的速度跟不上分配的速度它依然会把用户逻辑暂停,用户逻辑暂停了以后也就意味着不会有新的对象出现,同时会把用户线程抢过来加入到垃圾回收里面加快垃圾回收的速度,这样一来原来的并发还是变成了STW,还是得把用户线程暂停掉,要不然扫描和回收没完没了停不下来,因为新分配对象比回收速度快,所以这种东西叫辅助回收

如何进行GC调优

减少对象的分配,合理重复利用;避免string与[]byte转化;

两者发送转换的时候,底层数据结构会进行复制,因此导致gc效率会变低。

少量使用 + 连接 string

go里面string是最基础的类型,是一个只读类型,针对他的每一个操作都会创建一个新的string,如果是少量小文本拼接,用+就好;如果是大量小文本拼接用string.Join;如果是大量大文本拼接,用bytes.Buffer。

GC触发条件

  • 超过内存大小的阈值
  • 达到定时时间,阈值是一个由gcpercent的变量控制的,当新分配的内存占已使用中的内存的比例超过gcpercent时就会触发。如果一直达不到内存大小的阈值,GC会被定时触发(默认2min一次)。

参考

https://juejin.im/post/5d56b47a5188250541792ede

https://www.jianshu.com/p/8b0c0f7772da

https://mp.weixin.qq.com/s/Wh7A6czDIRofxveY_bEDNQ

https://blog.csdn.net/u010649766/article/details/80582153

https://lihaoquan.me/2016/11/3/go-gc-general.html

https://wudaijun.com/2017/12/gc-study/

next

go逃逸分析