Golang的垃圾回收(GC)機制
轉載自ofollow,noindex">https://blog.csdn.net/liangzhiyang/article/details/52670021
請先閱讀 golang 的 goroutine 排程機制 然後再到這裡
golang 的垃圾回收 採用的是 標記 - 清理( Mark-and-Sweep ) 演算法
就是先標記出需要回收的記憶體物件快,然後在清理掉;
在這裡不介紹 標記和清理的具體策略(可以參考https://lengzzz.com/note/gc-in-golang),只介紹 GC 過程是怎麼排程的以及 stw 相關
這個演算法,會導致 stw (stop the world) 的問題,中斷使用者邏輯
觸發 GC 機制
1. 在申 請 記憶體的 時 候, 檢查 當前當前已分配的記憶體是否大於上次 GC 後的記憶體的 2 倍,若是 則 觸 發 (主 GC 執行緒為當前 M )
2. 監控執行緒發現上次 GC 的 時間 已 經 超 過 兩分 鍾 了,觸 發 ;將一個 G 任 務 放到全域性 G 佇列中去。(主 GC 執行緒為執行這個 G 任 務 的 M )
每當觸發的時候,在主 GC 執行緒中就會走如下的 GC 流程:
1. stop the world ,等待所有的 M 休眠;此 時 所有的 業務邏輯 代 碼 都停止
2. 標記:分配 gc 標記任務,喚醒 gcproc 個 M (就是第一步休眠的那些),分 別 做 這 個,直到所有的 M 都做完,才 結 束;並且所有 M 再次 進 入休眠
3. 清理:有一個 單 獨的 goroutine 去清理已 經標記 的記憶體 對 象快
4. start the world , 設 置 gcwaiting=0 , 喚 醒所有的 M (不會超 過 P 個數)
對於上面的三個步驟,分別解釋:
stop the world :
1. 設定 gcwaiting=1 , 這 個在每一個 G 任 務 之前會 檢查 一次 這 個狀 態 ,如是, 則 會將當前 M 休眠;
2.
如果
這
個
M
裡面正在執行一個
長時間
的
G
任
務
,咋
辦
呢,
難
道會等待
這
個
G
任
務
自己切
換嗎
?
這樣
的
話
可要等
10ms
啊,不能等!
堅
決不能等!
所以會主
動發
出
搶
佔
標記
(
類
似於上一篇),
讓
當前
G
任
務
中斷,再執行下一個
G
任
務
的
時
候,就會走到第
1
步
3. 一直等待所有的 M 進入休眠,此時所有的業務邏輯程式碼都停 止
標記:
1. 根據 gcproc 的個數,分配成 gcproc 任 務 段; 喚 醒 gcproc-1 個 M 來 執 行(當前 M 也算一個 )
2. 對於一個 M , 喚 醒前 設定它的 helpgc 標記,喚醒之後這個 M 會立 馬 判斷 這 個 標記 ,如是, 則 開始做分配 給 自己的 標記 任 務,如果先做完了,就會從別的 M 裡面找一些來做
3. 等每一個 M 都做完,會再次 進 入休眠
清理:
1. 通 過設 置引數,可以以一個 單 獨 goroutine 執行, 這 個功能是在 1.3 版本之後增加的, 這樣 的 話 就直接到下一步了,清理 過 程不是 stw 的
2. 也可以序列的在主 GC 執行緒執行;這樣的話則 清理 過 程也是 stw 的,
start the world :
1. 設定 gcwaiting=0
2. 喚醒 P 個 M 來 繼續 做 G 任 務 (此 時 沒有 helpgc 標記),業務邏輯程式碼開 始
綜上:
是基於 1.4 版本的, GC 過程在 標記過程是( STW )的
在 1.5 版本里面對 GC 做了很大的優化;採用三色標記,將標記過程細化成三段,只有前後的兩段是 stw 的;極大地縮短了 gc 的 stw 時間