【Golang詳解】go語言垃圾回收GC(二) 實現原理
實現原理
垃圾收集的多個階段:
清理終止階段(STW)
- 暫停程式,所有的處理器在這時會進入安全點
我的理解是這裡stw,等待所有協程都知道要開始開啟寫屏障了,不然無法做到統一
- 如果當前垃圾收集迴圈是強制觸發的,我們還需要處理還未被清理的記憶體管理單元
標記階段-併發執行
-
將狀態切換至
_GCmark
-
開啟寫屏障
-
併發標記
-
標記輔助(下面介紹)
-
在這期間遵守混合寫屏障的機制
標記終止階段 -STW
- 暫停程式、將狀態切換至
_GCmarktermination
並關閉輔助標記的使用者程式;通知其他goroutine關閉寫屏障,停止寫屏障 - 清理處理器上的執行緒快取
清理階段-併發執行
- 將狀態切換至
_GCoff
開始清理階段,初始化清理狀態並關閉寫屏障; - 恢復使用者程式,這時候新建立的物件會標記為白色
- 後臺併發清理所有的記憶體管理單元
觸發時機
主動觸發
runtime.GC
— 使用者程式手動觸發垃圾收集;
被動觸發
-
預設2min沒有觸發過GC,則觸發一次GC
-
分配記憶體時,若記憶體分配達到一定比例則觸發
GC對CPU的使用率
在GC準備階段,go語言給每個P建立一個mark worker協程(就是標記的協程),把對應的g指標儲存到p中,這些mark worker建立後很快陷入休眠,等到標記階段得到排程執行
GC預設的CPU目標使用率為25%
GC在mark worker中引入了三種不同的工作模式:
gcMarkWorkerFractionalMode
-- 當垃圾收集的後臺 CPU 使用率達不到預期時(預設為 25%),啟動該型別的工作協程幫助垃圾收集達到利用率的目標,因為它只佔用同一個 CPU 的部分資源,所以可以被排程;
gcMarkWorkerDedicatedMode
-- 處理器專門負責標記物件,不會被排程器搶佔
gcMarkWorkerIdleMode
-- 當處理器沒有可以執行的 Goroutine 時,它會執行垃圾收集的標記任務直到被搶佔;
三種不同模式的工作協程會相互協同保證垃圾收集的 CPU 利用率達到期望的閾值
標記輔助
使用者程式有可能在GC期間分配新的記憶體,為了保證使用者程式分配記憶體的速度不會超出後臺任務的標記速度,執行引入了標記輔助技術。
在目前Go實現中,當GC觸發後,首先進入併發標記階段,併發標記會設定一個標誌gcAssistBytes
欄位,這個欄位儲存了當前協程輔助標記的物件位元組數。在併發標記階段期間,當 Goroutine 分配新物件時,會檢查此時 是否處於入不敷出的狀態。
如果是,會暫停分配記憶體過快的哪些goroutine,並將其轉去執行一些輔助標記的工作,從而達到放緩繼續分配、輔助GC的標記工作的目的