您的位置 首页 golang

Golang gc 简明过程(基于go 1.14)

go的gc已经有很多文章了,都比较长而且介绍的太玄学,感觉一直似懂非懂,今天读了一篇文章,然后发现其实原理很简单

首先说下 三色标记法 ,三色标记是对标记清楚法的改进,标记清楚法在整个执行时要求长时间stop the world,go从1.5版本开始改为三色标记法,初始将所有内存标记为白色,然后将roots(stack、global vars指针)加入待扫描队列(进入队列即被视为变成灰色),然后使用并发goroutine扫描队列中的指针,如果指针还引用了其他指针,那么被引用的也进入队列,被扫描的对象视为黑色(不能gc)。

三色标记的核心是将长时间的stop the world变为小段的stop the world ,并利用了write barrier来辅助实现,详情如下:

第一阶段:gc开始,std(stop the world)

初始所有内存都是白色的,进入标记队列就是灰色

  1. stop the world
  2. 每个processor启动一个mark worker goroutine用于标记(用于第二阶段工作)
  3. 启动gc write barrier(记录一下后续在进行marking时被修改的指针,后续GC与用户代码并行)
  4. 找到所有roots(stack, heap, global vars)并加入标记队列
  5. start the world,进入第二阶段

第二阶段:marking,start the world

  1. 从标记队列里面取出对象,标记为黑色(不能GC)
  2. 然后检查是否有是指向另一个对象,是,则加入标记队列
  3. golang中分配对象会根据是否是指针分别放到不同的span中,根据这个如果span是指针span,那么就需要继续scan下一个对象,否则停止该路scan,取队列中下一个对象继续scan
  4. 在扫描过程中,如果用户代码修改对象,那么会触发写屏障,将对象标记为灰色,并加入单独的扫描队列中

注意:

  1. 这个阶段是和用户的程序并发一起运行的
  2. 所有进入队列中的对象逻辑上就认为是灰色的
  3. 从队列中出来的需要scan的对象被标记为黑色,将bitmap中对应的gcmarkBits设为1

第三阶段:处理marking过程中修改的指针,stop the world

stop the world,将gc write barrier记录的所有修改的指针也加入标记队列进行一轮标记

注意这个阶段不是和用户程序并行的

扫描完了以后start the world

第四阶段:sweep

到这一阶段,所有内存要么是黑色的要么是白色的,清楚所有白色的即可

golang的内存管理结构中有一个bitmap区域,其中可以标记是否“黑色”

GC触发条件

  1. 内存大小阈值, 内存达到上次gc后的2倍
  2. 达到定时时间 ,2m interval

阈值是由一个gc percent的变量控制的,当新分配的内存占已在使用中的内存的比例超过gcprecent时就会触发。比如一次回收完毕后,内存的使用量为5M,那么下次回收的时机则是内存分配达到10M的时候。也就是说,并不是内存分配越多,垃圾回收频率越高。 如果一直达不到内存大小的阈值呢?这个时候GC就会被定时时间触发,比如一直达不到10M,那就定时(默认2min触发一次)触发一次GC保证资源的回收。

并发回收实现原理

1. 赋值器修改对象图,导致某一黑色对象引用白色对象; 2. 从灰色对象出发,到达白色对象的、未经访问过的路径被赋值器破坏。 解决办法: 1. 黑色对象引用某个白色对象后,将白色对象“变灰”,需要后续扫描 2. 新分配的对象直接设为“黑色” 实现原理: 内存屏障(Memory Barrier)保障了代码描述中对内存的操作顺序,既不会在编译期被编译器进行调整,也不会在运行时被 CPU 的乱序执行所打乱, 是一种语言与语言用户间的契约。

在开始gc是启动内存写屏障,所有修改对象的操作加入一个写屏障,通知gc将受影响的对象变为灰色;

然后在第一次gc后,重新对gc过程中变灰的对象再扫描一次

参考:

搞懂Go垃圾回收 – 掘金

文章来源:智云一二三科技

文章标题:Golang gc 简明过程(基于go 1.14)

文章地址:https://www.zhihuclub.com/86736.shtml

关于作者: 智云科技

热门文章

网站地图