https://www.cnblogs.com/cxy2020/p/16321884.html
https://developer.aliyun.com/article/861507
插入/删除写屏障都存在漏(少)回收,但是不会误(多)回收
插入写屏障可以认为是一个保守设计,所有新分配的堆对象如果被黑/灰对象指向都标记为灰色,在下一轮GC再回收。缺点是标记完回栈上扫一遍需要stw。
删除写屏障也可以认为是一个保守设计,需要所有可达根对象被标记了(期间stw)才能开启删除写屏障。缺点是
为了避免内存空间错误释放,需要stw,为了不stw,推出三色不变性,负责该原则就不需要stw。
强三色不变式(对应插入写屏障)
不允许黑色对象引用白色对象
- 插入写屏障
具体操作:A,新增对象B且添加A->B,插入的新对象B标记为灰色。
writePointer(slot, ptr):
shade(ptr)
*slot = ptr
插入屏障仅会在堆内存(父节点是堆对象)中生效,不对栈内存空间生效(在栈上指向新对象时不会置黑),这是因为go在并发运行时,数十万goroutine的栈都进行屏障保护自然会有性能问题。
弊端:由于栈上的对象没有插入写机制,在扫描完成后,仍然可能存在栈上的白色对象指向黑色的堆对象,所以在最后需要对栈上的空间进行STW,防止对象误删除。
弱三色不变式
黑色对象可以引用白色对象,但是白色对象的上游必须存在灰色对象
- 删除写屏障
具体操作:A->B,删除->,则被删除的对象B标记为灰色。
writePointer(slot, ptr):
shade(*slot)
*slot = ptr
删除写屏障(基于起始快照的写屏障)有一个前提条件,就是起始的时候,把整个根部扫描一遍,让所有的可达对象全都在灰色保护下(根黑,下一级在堆上的全灰)
,之后利用删除写屏障捕捉内存写操作,确保弱三色不变式不被破坏,就可以保证垃圾回收的正确性(但是需要stw)。
一个对象的引用被删除后,即使没有其他存活的对象引用它,它仍然会活到下一轮。如此一来,会产生很多的冗余扫描成本,且降低了回收精度。
混合写屏障
插入写屏障:结束时需要STW来重新扫描栈,标记栈上引用的白色对象的存活;
删除写屏障:回收精度低,GC开始时STW扫描堆栈来记录初始快照,这个过程会保护开始时刻的所有存活对象。
GC期间,任何在栈上新创建的对象,均为黑色。GC刚开始的时候,会将栈上的可达对象全部标记为黑色(解除删除写屏障的stw)。屏障限制只在堆内存中生效,且栈上全部标记黑色无需stw,提升了GC效率。
1、GC开始将栈上的可达对象全部扫描并标记为黑色 (之后不再进行第二次重复扫描,无需STW)
2、GC期间,任何在栈上创建的新对象,均为黑色。(惰性回收)(解除了删除写屏障的stw)
3、插入写屏障+删除写屏障(堆上元素指向其他对象的时候开启)
- 全程没有stw