資料閱讀
有兩張不錯的gif圖(源自wikipedia) https://zhuanlan.zhihu.com/p/74853110
最先看的文章般堆,引用了上面的文章 http://www.reibang.com/p/4c5a303af470
在討論 Go 的混合寫屏障 https://github.com/changkun/go-under-the-hood/issues/20
解釋了為啥有 read barrier https://www.zhihu.com/question/42879518/answer/437304734
這些個文章看完,總覺得其中對golang的混合寫屏障沒說透,甚至感覺有錯誤說法盏触。
三色標記的理解
基本的mark-sweep算法
基本算法有個缺點,需要STW(stop the world),有較大延時。
三色標記法(tricolor mark-sweep)
三色標記的過程中拄查,節(jié)點顏色變化:白色》灰色》黑色。當沒有灰色了棚蓄,標記結(jié)束堕扶,黑色存活,白色清除梭依。
三色標記可以實現(xiàn)增量回收和并發(fā)回收稍算,能降低延時(latency);當然也有缺點役拴,會降低吞吐量(throughput)糊探。
三色標記正確運行需要滿足下面2個條件中的一個:
- 強三色不變式:黑色對象不引用白色對象。
- 弱三色不變式:黑色對象引用的白色對象可以通過灰色對象搜索到河闰。
當使用增量回收和并發(fā)回收時科平,回收過程中,用戶程序會新建對象修改對象引用淤击,會破壞上面的條件匠抗。寫屏障就是為了處理這個事情故源。
有兩套方案
// 當obj是黑色時污抬,標記插入的*prt對象至少為灰色。滿足強三色條件
// GC運行時绳军,新new出來的對象印机,可以直接標記成黑色。
djjkstra_write_barrier(obj, ref, ptr){
shade(ptr) // shade的做法時如果是白色就標記成灰色门驾,否則不變射赛。后面的偽代碼都是這個意思。
ref = ptr
}
// 從obj上刪除ref時奶是,標記*ref至少為灰色楣责。這樣可以滿足弱三色條件
// 悲觀認為所有被刪的對象都可能被黑色對象引用了竣灌,效率很低的樣子。
// **注意:**GC運行時秆麸,新new出的對象必須直接標記成黑色
yuasa_write_barrier(obj, ref, ptr){
shade(ref) // 當obj時黑色時初嘹,可以不標記,因為刪掉黑色到白色之類引用關(guān)系不破壞弱三色條件
ref = ptr
}
一般來說dijk就挺好的沮趣,但像golang這種希望寫棧上的引用時不使用寫屏蔽屯烦,提高性能,所以得找別的方法房铭。
- golang早期用的dijk的方法驻龟,得把棧本身重新標記成灰色,mark的最后階段STW缸匪,然后掃描下棧翁狐,完成標記。這樣說是最后結(jié)算的延時可以達到100ms凌蔬。
- golang 1.8 用yuasa的方法谴蔑,也同樣存在同樣的問題。這時可以當棧還不是黑色時龟梦,所有復(fù)制操作隐锭,額外把ptr標記成灰色,就當是從棧里刪除下來的计贰。
偽代碼
// golang 1.8的方式顶别,說法是結(jié)合了dij和yuasa。效率(吞吐量)比yuasa還低棠众。
// **注意:**GC運行時耻讽,新new出的對象必須直接標記成黑色
golang_hybird_write_barrier(obj, ref, ptr){
shade(ref)
// 當棧本身還沒完成掃描時,假定ptr就是從棧上刪除取下來的對象秧秉。當棧掃描完褐桌,可以當成標記為黑色,這時就不需要補充標記了象迎。
if current stack is grey:
shade(ptr)
ref = ptr
}
這樣就可以實現(xiàn)荧嵌,棧里面的寫操作不需要barrier處理,掃描最后也不需要STW重新掃描棧砾淌。