Go的三色標記GC

2021-09-14 04:28:38 字數 2127 閱讀 9630

三色標記的原理如下:

整個程序空間裡申請每個物件佔據的記憶體可以視為乙個圖, 初始狀態下每個記憶體物件都是白色標記,先stop the world,將掃瞄任務作為多個併發的goroutine立即入隊給排程器,進而被cpu處理,第一輪先掃瞄所有可達的記憶體物件,標記為灰色放入佇列;第二輪可以恢復start the world,將第一步佇列中的物件引用的物件置為灰色加入佇列,乙個物件引用的所有物件都置灰並加入佇列後,這個物件才能置為黑色並從佇列之中取出。迴圈往復,最後隊列為空時,整個圖剩下的白色記憶體空間即不可到達的物件,即沒有被引用的物件; 第三輪再次stop the world,將第二輪過程中新增物件申請的記憶體進行標記(灰色),這裡使用了writebarrier(寫屏障)去記錄這些記憶體的身份;

整個gc的流程如下圖:

注意到:

mark 有兩個過程。

首先從 root 開始遍歷,root 包括全域性指標和 goroutine 棧上的指標,標記為灰色。遍歷灰色佇列。

re-scan 全域性指標和棧。因為 mark 和使用者程式是並行的,所以在上一步執行的時候可能會有新的物件分配,這個時候就需要通過寫屏障(write barrier)記錄下來。re-scan 再完成檢查一下。

stop the world 有兩個過程。

第乙個是 gc 將要開始的時候,這個時候主要是一些準備工作,比如 enable write barrier。

第二個過程就是上面提到的 re-scan 過程。如果這個時候沒有 stw,那麼 mark 將無休止。

mark完畢後start the world進行並行清理,對於並行清理,gc 初始化的時候就會啟動 bgsweep()這個協程並一直在後台阻塞, 開始清理時將這個協程喚醒並給主m去做併發的sweep。

記憶體管理都是基於 span 的,mheap_ 是乙個全域性的變數,所有分配的物件都會記錄在 mheap_ 中。在標記的時候,我們只要找到對物件對應的 span 進行標記,清掃的時候掃瞄 span,沒有標記的 span 就可以**了。

另外:1.8以後的golang將第一步的stop the world 也取消了,這又是一次優化:

關於寫屏障的用處 如下面的例子,這個例子修改自知乎上的乙個問答,在此表示感謝:

gc前:

stack->a->b ; a為棧中申請的物件,b為堆中申請的物件,a物件中存在對b的引用;

stack->c ; c 也是棧中申請的物件。

stop the world, mark。 這裡a,c都會被標記為灰色;b為白色

start the world 反覆mark。

由於是併發的mark,我們假設c先被處理,c沒有引用其他物件,所以直接置黑,從佇列中取出;此時c為黑色,a為灰色,b為白色

假設這時使用者做了如下操作:

a=nil

new(d);

c->b; 即,將a中對b的引用置為空(你也可以理解為將a中對其他任何記憶體物件的引用都清空),隨即申請d物件,然後在c中增加對b的引用。

由於c已經是黑色,所以不會再去掃瞄他,那麼本次記憶體掃瞄就不可能找得到b;而d物件由於剛申請出來,還沒有被引用,所以這裡只對a進行了mark:a:黑色,b:白色;c:黑色;d:白色

這時使用者又做了:

b->d; 由於b無法被掃瞄到,這裡顯然d也不會被掃瞄到。 這樣的狀況會一直持續到這輪反覆mark結束(即灰色隊列為空)。

stop the world, mark termination。 sweep。 整個gc結束, b,d的記憶體空間都是白色,所以在sweep時會被清理掉。如何避免這種誤清理呢?

寫屏障的功能就是在 c->b發生時,對b標記為灰色,入隊, 以及在b->d發生時,對d標記為灰色,入隊,這樣,在整個反覆mark階段結束時,我們能確保這段時間新發生的對白色物件的記憶體引用操作都被處理到(變黑),b和d就不會被誤清理。寫屏障在第一次掃瞄完,標記入隊後,反覆標記時開啟寫屏障, sweep前將寫屏障關閉。

簡而言之,寫屏障的作用大致是: 可以確保不會有物件a直接引用白色物件b(發生時將白色物件置灰)。這裡有個小細節,go 1.5中不管物件a是什麼顏色,只要他引用了物件b,就將b置灰。

Golang三色標記GC和混合寫屏障筆記

首先要區分版本 每個版本更新的內容是不一致的 首先,stw stop the world 再由程式出發 標記可達的物件 然後將不可達的物件進行 缺點 標記需要掃瞄整個heap 清除資料會產生heap碎片 白灰黑 灰是臨時的存放點 直到只剩下白和黑 程式起初物件都是白色 gc遍歷root set 只走...

雙色 三色排序問題

實現乙個函式,給定乙個陣列,要求使得陣列中負數在所有正數的前面 實現乙個函式,給定乙個陣列,要求使得陣列中負數在前正數在後零在中間 1 include2 include34 輸出陣列元素 5void print int arr,int len 6 交換兩個數 7void swap int left,...

三色旗問題

三色旗問題 假設有乙個陣列,它有n個元素,每乙個不外乎是紅,白,藍3種顏色之一的代號,就用r,w,b代表。這些元素在陣列中並沒有依同樣顏色的元素排在一起的方式來排列,請寫乙個程式把這些元素排成所有藍色在前,接著是白色,最後是紅色的排列方式,不過在寫程式時要滿足下面的條件 1 不能用額外的記憶體,換句...