golang垃圾回收機制
golang的GC,1.8通過混合寫⼊屏障, 使得STW降到了sub ms。go語言中程序代碼執行和垃圾回收是並發執行的。
當前Go GC特徵 :
三色標記,並發標記和清掃,非分代,非緊縮,混合寫屏障。
三色標記:
1. 將所有對象放在白色集合中
2. 從rootset遍歷可達對象,將可達對象放在灰色集合中
3. 將灰色集合中對象進行遍歷,將可達對象放在灰色集合中,將其本身放在黑色集合中
4. 重複第三步,直到灰色集合遍歷為空
5. 將白色集合中的對象視為垃圾進行清掃
6. 重置黑色集合對象,將其變為白色,進行下次GC
gc寫屏障解決垃圾回收並發的問題:
寫屏障是在寫入指針前執行的一小段代碼,用以防止並發標記時指針丟失,這段代碼Go是在編譯時加上的。
如果以下代碼沒有寫屏障,則當代碼執行和垃圾回收並發執行時,GC垃圾回收掃描到obj1為nil,將其標記為回收對象,此時代碼重新給obj1和obj2賦值,obj2為nil,垃圾回收將obj2標記為回收對象,此時obj1和obj2都會被回收,但obj1此時是不需要回收的
func (obj *Object) Demo() { //初始化 obj1 = nil obj2 = obj //gc 垃圾回收開始⼯作 //掃描對象 obj1 完成後 //代碼修改為:對象重新賦值 obj1 = obj obj2 = nil //掃描對象 obj2 }
GC觸發
1. 分配內存時, 當前已分配內存與上一次GC結束時存活對象的內存達到某個比例時就觸發GC。
2. sysmon檢測2min內是否運行過GC, 沒運行過 則執行GC。
3. runtime.GC()強制觸發GC。
垃圾回收默認是全併發模式運行,GC goroutine 一直循環執行,直到符合觸發條件時被喚醒。
並發標記分為兩個步驟:
掃描:遍歷相關內存區域,依次按照指針標記找出灰色可達對象,加入隊列。
標記:將灰色對象從隊列取出,將其引用對象標記為灰色,自身標記為黑色。
// 將灰色對象從隊列取出,其引用對象標灰,自身標黑 func gcBgMarkStartWorkers() { // Background marking is performed by per-P G's. Ensure that // each P has a background GC G. for _, p := range allp { if p.gcBgMarkWorker == 0 { go gcBgMarkWorker(p) notetsleepg(&work.bgMarkReady, -1) noteclear(&work.bgMarkReady) } } }
垃圾清理
所有未標記的白色對象不再被引用,可以將其內存回收。
// 無限循環,被喚醒後執行並發清理任務,完成後回收內存等待下次執行 func bgsweep(c chan int) { sweep.g = getg() lock(&sweep.lock) sweep.parked = true c <- 1 goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1) for { for sweepone() != ^uintptr(0) { sweep.nbgsweep++ Gosched() } for freeSomeWbufs(true) { Gosched() } lock(&sweep.lock) if !isSweepDone() { // This can happen if a GC runs between // gosweepone returning ^0 above // and the lock being acquired. unlock(&sweep.lock) continue } sweep.parked = true goparkunlock(&sweep.lock, waitReasonGCSweepWait, traceEvGoBlock, 1) } }