垃圾回收器 垃圾回收是釋放掉那些不再被使用的記憶體空間的過程。 換句話說,垃圾回收器會去檢查哪些對象超出範圍並且不會再被引用到,然後它回去釋放掉那些對象占用的記憶體空間。這個過程實在 go 程式運行中以併發的方式去進行的,不是 go 程式執行之前,也不是 go 程式執行之後。go 垃圾回收器實現的說明文 ...
垃圾回收器
垃圾回收是釋放掉那些不再被使用的記憶體空間的過程。
換句話說,垃圾回收器會去檢查哪些對象超出範圍並且不會再被引用到,然後它回去釋放掉那些對象占用的記憶體空間。這個過程實在 go 程式運行中以併發的方式去進行的,不是 go 程式執行之前,也不是 go 程式執行之後。go 垃圾回收器實現的說明文檔給出瞭如下聲明(runtime 包下的 mgc.go):
The GC runs concurrently with mutator threads, is type accurate (aka
precise), allows multiple GC thread to run in parallel. It is a concurrent
mark and sweep that uses a write barrier. It is non-generational and non
compacting. Allocation is done using size segregated per P allocation
areas to minimize fragmentation while eliminating locks in the common
case.
垃圾回收是和go線程同時運行的,它是類型精確的,而且多個垃圾回收線
程可以並行運行。它是一種使用了寫屏障的併發標記清除的垃圾回收方
式。它是非分代和非壓縮的。使用按P分配區域隔離的大小來完成分配,
以最小化碎片,同時消除常見情況下的鎖。
三色演算法
go垃圾回收器的操作都是基於三色演算法。
嚴格來說,在Go中這個演算法的官方名稱是叫做三色標記清除演算法(tricolor mark-and-sweep algorithm)。它可以和程式一起併發工作並且使用寫屏障(write barrier)。這就意味著,當Go程式員運行起來,go調度器去負責應用程式的調度,而垃圾回收器會像調度器處理常規應用程式一樣,去使用多個goroutines去進行工作。
三色標記清除演算法背後的首要原則就是它把堆中的對象根據它們的顏色分到不同集合裡面,顏色是根據演算法進行標記的。
黑色集合是為了確保沒有任何指針指向白色集合。但是白色集合中的對象允許有指針指向黑色集合,因為這不會對垃圾回收器的操作產生影響。灰色集合可能會有指針指向白色集合里的對象。白色集合中的對象就是垃圾回收的候選對象。
註意到沒有任何對象可以從黑色集合進到白色集合,這允許演算法能夠去操作並且清除白色集合里的對象。此外,沒有任何黑色集合里的指針對象能夠直接指向白色集合中的對象。
當垃圾回收開始,全部對象標記為白色,然後垃圾回收器會遍歷所有根對象並把它們標記為灰色。根對象就是程式能直接訪問到的對象,包括全局變數以及棧裡面的東西。這些對象大多數取決於特定程式的go代碼。在這之後,垃圾回收器選取一個灰色的對象,把它變為黑色,然後開始尋找去確定這個對象是否有指針指向白色集合的對象。這意味著當一個灰色對象由於被其它對象的指針所指而掃描到的時候,這個灰色對象會被標記為黑色。如果掃描發現這個灰色對象有一個或者更多指針指向白色對象時,會把所指向的白色對象放到灰色集合里。只要有灰色集合對象存在,這個過程就會一直進行下去。之後,白色集合里的對象就是沒人訪問的對象,並且它們所占用的記憶體可以被回收重用。因此,在這個點上,我們說白色集合里的元素被垃圾回收了。
在這個過程中,運行應用程式被叫做修改器(mutator)。mutator去運行一個小的方法叫做寫屏障**(write barrier)**,每次堆中的指針被修改寫屏障都會去執行。如果堆中對象的指針被修改,就意味著那個對象現在是可觸達的,寫屏障會把它標記為灰色並把它放到灰色集合中。
mutator負責保持黑色集合中沒有任何元素的指針去指向白色集合中的元素。這是在寫屏障方法的幫助下完成的。如果維持這個不變狀態失敗的話,會毀壞垃圾回收過程,並且很可能會以一種醜陋和非預期的方式破壞你的程式。
go垃圾回收也可以應用於其它變數例如channel!當垃圾回收器發現一個channel是不可達的而且channel變數再也不會被訪問到,它就會釋放掉它的資源甚至說channel還沒被關閉!
Go允許你通過在你的Go代碼里放一個 runtime.GC() 的聲明來手動去開啟一次垃圾回收。但是,要記住一點, runtime.GC() 會阻塞調用器,並且它可能會阻塞整個程式,尤其是如果你想運行一個非常繁忙的而且有很多對象的go程式。這種情況發生,主要是因為你不能在其他任何事都在頻繁變化的時候去處理垃圾回收,因為這種情況不會給垃圾回收器機會,去清楚地確定白色、黑色和灰色集合里的成員。這種垃圾回收狀態也被稱作是垃圾回收安全點**(garbage collection safe-point)**。
垃圾回收器背後的更多操作
go垃圾回收器的主要關註點是低延遲,也就是說為了進行實時操作它會有短暫的暫停。另一方面,創建新對象然後使用指針操作存活對象是程式始終在做的事情,這個過程可能最終會創建出不會再被訪問到的對象,因為沒有指向那些對象的指針。這種對象即為垃圾對象,它們等待被垃圾回收器清理然後釋放它們的空間。之後它們釋放的空間可以再次被使用。
垃圾回收中使用的最簡單的演算法就是經典的標記清除演算法**(mark-and sweep)**:演算法為了遍歷和標記堆中所有可觸達對象,會把程式停下來(stop the world)。之後,它會去清掃(sweeps)不可觸達的對象。在演算法的標記(mark)階段,每個對象被標記為白色、灰色或黑色。灰色的子對象標記為灰色,而原始的對象此時會標記為黑色。沒有更多灰色對象去檢查的話就會開始清掃階段。這個技術適用是因為沒有從黑色指向白色的指針,這是演算法的基本不變要素。
儘管標記清除演算法很簡單,但是它會暫停程式的運行,這意味著實際過程中它會增加延遲。go會通過把垃圾回收器作為一個併發的處理過程,同時使用前一節講的三色演算法,來降低這種延遲。但是,在垃圾回收器併發運行時候,其它的過程可能會移動指針或者創建對象,這會讓垃圾回收器處理非常困難。所以,讓三色演算法併發運行的關鍵點就是,維持標記清除演算法的不變要素即沒有黑色的對象能夠指向白色集合對象。
因此,新對象必須進入到灰色集合,因為這種方式下標記清除的不變要素不會被改變。另外,當程式的一個指針移動,要把指針所指的對象標記為灰色。你可以說灰色集合是白色集合和黑色集合中間的“屏障”。最後,每次一個指針移動,會自動執行一些代碼,也就是我們之前提到的寫屏障,它會去進行重新標色。為了能夠併發運行垃圾回收器,寫屏障代碼產生的延遲是必要的代價。
一定要記住,Go垃圾回收器是一個實時的垃圾回收器 ,它是和其他goroutines一起併發運行的
還在找我的道