golang gc:三色标记法,1.5版本开始(采用插入写屏障)
三色:黑白灰
过程:
1.开始所有节点均为白色;
2.扫描所有根节点,标记为灰色;(根节点为编译时确定的全局变量,堆上指针,goroutine栈上指针,扫描过程中新指向堆的指针)
3.扫描灰色节点,找到灰色节点引用的节点,将引用的节点置为灰色,当前灰色节点置为黑色;
4.循环扫描灰色这个过程,(分层,每次一层递归),直到灰色节点为空;
5.三色标记法,gc过程是和业务代码并行执行,业务代码会产生新的指针指向变动,会产生新的堆指针分配,1.5版本采用了插入写屏障,(栈空间分配频繁,不对栈空间进行屏障,需要对栈空间进行stw保护,之后再次rescan栈空间),需要rescan,需要stw;
6.回收白色节点;
写屏障:并发gc会产生黑色节点引用白色节点情况,导致正常的指针变量错误的被清除;解决方法为写屏障;
主要包括强三色不变式和弱三色不变式;
强三色不变:黑色节点不能引用白色节点,如果引用白色节点需要将白色节点置灰(插入写屏障);
弱三色不变:黑节点可以引用白节点,但白节点有其他灰色节点或递归指向存在灰色节点,删除白色节点引用时,需要把白色节点置灰(删除写屏障);
栈上变量较小,且频繁开辟或删除,不开启写屏障;需要之后一次rescan;
stw时机:
插入写屏障:结束时需要STW来重新扫描栈,标记栈上引用的白色对象的存活;(1.5版本采用)
删除写屏障:回收精度低,GC开始时STW扫描堆栈来记录初始快照,这个过程会保护开始时刻的所有存活对象;
混合写屏障:1.8版本加入
原因是stw需要耗时;加入混合写屏障,解决这个问题;
流程:
1.开始标记时候,栈上可达节点均置黑,之后不进行rescan,不用stw;
2.gc时产生的在栈上创建的对象,均置黑;
3.堆空间删除的对象置灰;
4.堆空间插入的对象置灰;
异步抢占:1.14加入
之前基于栈增长,一个goroutine如果为for{}空循环,将不会stw,异步抢占后可以抢占当前gotoutine,优化了gc;
理解有限~