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)
	}
}
Tags: