gc


白色–垃圾
黑色-存活(不是垃圾)
灰色–追踪(待确认)

v1.3标记清扫算法+STW机制

v1.5三色标记法+强/弱三色不变式

工作过程:
1,先将所有对象标记为白色

2,第二步,从根节点开始遍历,将对象标记为灰色,表示要继续追踪,将灰色对象写入grey set

3,从grey set开始遍历,追踪到的对象设置为灰色(2,7),移动到grey set,将它的根节点标记为黑色(1,4),移动到black set

4,重复上一步,从grey set开始追踪,将追踪到的对象3标记灰色,移动到grey set,它的根节点2标记为黑色,移动到black set;对象7追踪不到了,直接标记黑色,移动到black set。下一次从grey set追踪,将3标记为黑色,移动到black set,此时不存在灰色对象,表示本轮标记工作完成,开始清扫,清除白色对象

如果没有STW,就会出现:

如下2-3的引用和4-3的引用 &&

解决:




A-黑色 ,A新增引用B,

1 对象4 新增的引用因为在堆上,触发插入写屏障,对象8被标记为灰色

2 下一轮标记将对象8标记黑色,没有灰色对象,本轮标记完成


3 清扫前对栈区开启stw,重新扫描栈空间,标记完成后清除白色对象

环境:A–>B, A为黑色,现将A的应用置为nil,即删除A到B的引用

1-5的引用被删除,将5标记为灰色

v1.8三色标记+混合写屏障


混合写屏障同时破坏条件1和条件2

1,gc开始全部对象白色
2,优先扫描栈,将栈上可达对象全部标记黑色



场景2:
:1 在栈上新增对象9,因为开启混合写屏障,9直接标记为黑色
2 9引用对象3

场景3


增加10-7的引用,7标记灰色,从white set移到grey set

场景4:


内存回收(移动式 /复制式)


gc清扫后的碎片内存有两种方法处理
1,清扫后移动数据使其紧凑
2,复制时回收,内存分两块,from标记后复制到to,to和from交换

STW用户程序被挂起,执行gc工作的阶段

读写屏障


多核

go语言gc

gc过程


全局变量gcphrase是否进入gcMark标识,writebarrier记录是否开启写屏障,gcblackenabled标识是否开启写屏障

1 gc建立在P上,在每个P创建mark worker协程,将对应的指针放到p中,进入休眠等待唤醒
2 gc进入到gcmark阶段,开始第一次stop the world,gcphrase设置为gcMark,writebarrier=true,gcblackenable=1
3 开始start the world ,开启写屏障,协程开始标记工作,标记完成或者没有可标记对象时第二次stop the world,
gcpharse置为gcmarktermination,gcblackenable置为0
4进入gcoff阶段,关闭写屏障,start the world进入清扫阶段

sweep清扫工作:
1 由gcenable创建,创建后加入runqueue中,标记完成后等待被唤醒进行清扫工作
2 sweep也是增量进行
3 sweep前必须确认上一次sweep工作已完成

标记方法


1 bss段 data段想heap追踪
2 栈上的局部变量 参数 返回值追踪到heap

我们已经知道堆内存span按数据大小编号存储

1 还有一种span专门存储指针类型,不会被gc扫描
2 元数据的类型中的ptrdata字段标记是否是包含指针



1 arena中heaparena的bitmap存储了数据信息,低位记录是否是指针,高位记录是否继续扫描
2 heaparena中spans字段记录当前page对应哪一个span
3 通过元素的地址,就可以通过bitmap知道中标记的是否是指针
4 也可以通过地址查到对应的spans,根据spans找到在那个pages
5 span中的allocbits位图标记当前span是否已分配,gcmarkbits位图标记当前元素是否存活
以上步骤,就可以将元素进行gcmiark分作

gc工作队列


通过全局工作缓存 本地缓存 写屏障来缓解工作队列竞争问题

gc工作模式(deadcated/fractional)

1 go规定gc的cpu利用率为25%,每次P启动多少个markwork,依据GOMAXPROCS25%来计算
2 为了减少cpu浪费,go采用两种工作模式
a dedicate-执行标记任务 直到被强占
b fractional-执行到目标数(1.5-1)/6 时主动让出(6个p,每个p执行的目标既是(1.5-1)/6 )
3 依据GOMAXPROCS
25%来确认启动多少个dedicate和多少个fractional

借代偿还

1 若协程要分配内存时gc标记工作尚未完成,就要分担一部分标记工作,申请的内存越大,负担的越多-借代偿还
2 当前G中的gcassistbytes小于0 -负债 大于0 --有结余,负债的g要辅助gc完成一定的标记工作来偿还债务
3 后台markworker每完成定量的标记工作,在全局gccontroller存一部分信用,有负债的g可以从哲理steal信用

1每次申请大块内存时 先清扫
2 辅助标记和辅助清扫确保并发是内存鸭梨导致的来不及回收情况

混合写屏障

gc触发方式

1 手动触发—入口runtime.GC
2 分配内存时触发–gc标记结束后设置下一次gc标记的堆内存分配量
3 监控线程–时间段控制