GC 與 mutator 線程并發(fā)運(yùn)行,允許多個(gè) GC 線程并行運(yùn)行
創(chuàng)新互聯(lián)從2013年成立,先為鳳山等服務(wù)建站,鳳山等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為鳳山企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。
GC 是一個(gè)使用寫屏障的并發(fā)標(biāo)記和清除。
GC 是非分代的,非緊湊的。
Allocation 是按照大小隔離每個(gè) P 分配的區(qū)域來完成的,以在消除常見情況下的鎖的同時(shí),最小化碎片。
了解 GC 的好地方,可以從 Richard Jones 的 gchandbook.org 開始。
1. GC 執(zhí)行清除終止
? a. Stop the world ,這將導(dǎo)致所有 P 達(dá)到 GC 安全點(diǎn)。
? b. 清除任何未清除過的 spans ,只有在預(yù)期時(shí)間之前強(qiáng)制執(zhí)行此 GC 周期時(shí),才會(huì)有未清除的 span 。
2. GC 執(zhí)行標(biāo)記階段
? a.?? 準(zhǔn)備標(biāo)記階段,將 gcphase 設(shè)置為 _GCmark (從 _GCoff 開始),啟用寫屏障,啟用 mutator assist ,并對根標(biāo)記作業(yè)進(jìn)行排隊(duì)。
在所有 P 都啟用寫屏障之前,不會(huì)掃描任何對象,這是使用 STW 完成的。
? ?b. Start the world ,從現(xiàn)在開始,GC 工作由調(diào)度器啟動(dòng)的 標(biāo)記worker 和作 為 allocation 的一部分執(zhí)行的 assists 來完成。
寫屏障將覆寫的指針和任何指針寫的新指針值都著色。
新分配的對象立即被標(biāo)記為黑色。
? c.?? GC 執(zhí)行根標(biāo)記作業(yè)。包括: 掃描所有棧 , 著色所有全局變量 ,以及 著色堆外運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)中的任何堆指針 。
掃描棧會(huì)停止goroutine,對goroutine棧中找到的任何指針進(jìn)行著色,然后恢復(fù)goroutine。
? ? d.?? GC 耗盡灰色對象的工作隊(duì)列,將每個(gè) 灰色 對象掃描為 黑色 ,并對在該對象中找到的所有指針進(jìn)行著色(反過來可能會(huì)將這些指針添加到工作隊(duì)列中)。
? ?e.?? 由于 GC work 分散在本地緩存中,因此 GC 使用 分布式終止算法 來檢測何時(shí)不再有根標(biāo)記作業(yè)或灰色對象(參見 gcMarkDone 函數(shù))。
此時(shí),GC 狀態(tài)轉(zhuǎn)換到標(biāo)記終止( gcMarkTermination )。
3. GC 執(zhí)行標(biāo)記終止 gcMarkTermination
? a. Stop the world
? b. 將 gcphase 設(shè)置為 _GCmarktermination ,并禁用 workers 和 assists。
? c. 進(jìn)行內(nèi)務(wù)整理,如 flushing mcaches
4. GC 執(zhí)行清除階段
? ?a. 準(zhǔn)備清除階段,將 gcphase 設(shè)置為 _GCoff ,設(shè)置清除狀態(tài)并禁用寫屏障。
? b. Start the world ,從現(xiàn)在開始,新分配的對象是白色的,如有必要,在使用 spans 前 allocating 清除 spans 。
? ?c. GC 在后臺(tái)進(jìn)行 并發(fā)清除 并響應(yīng) allocation ,見下面的描述。
5. 當(dāng)分配足夠時(shí),重復(fù)上面 1 開始的步驟,參見下面關(guān)于 GC rate 的討論。
清除階段與正常程序執(zhí)行并發(fā)進(jìn)行。
在后臺(tái) goroutine 中,堆被惰性(當(dāng) goroutine 需要另一個(gè) span 時(shí))且并發(fā)地逐個(gè) span 掃描(這有助于不是 CPU bound 的程序)。
在 STW 標(biāo)記終止 的結(jié)尾,所有的 span 都被標(biāo)記為 需要清除 。
后臺(tái)清除器 goroutine 簡單地逐個(gè)清除 span 。
為了避免在存在未清除的 span 時(shí)請求更多的 OS內(nèi)存 ,當(dāng) goroutine 需要另一個(gè) span 時(shí),它首先嘗試通過清除來回收這些內(nèi)存。
當(dāng) goroutine 需要分配一個(gè)新的 小對象span 時(shí),它會(huì)清除相同大小的小對象 span ,直到釋放至少一個(gè)對象為止。
當(dāng) goroutine 需要從堆中分配 大對象span 時(shí),它會(huì)清除 span ,直到將至少那么多頁面釋放到堆中。
有一種情況,這可能是不夠的:如果 goroutine 清除并釋放兩個(gè)不相鄰的 單頁span 到堆中,那么它將分配一個(gè)新的 雙頁span ,但是仍然可以有其他 單頁未清除的span ,可以組合成 雙頁的span 。
確保在未清除的 span 上不進(jìn)行任何操作(這會(huì)破壞 GC 位圖中的標(biāo)記位)至關(guān)重要。
在 GC 期間,所有 mcache 都被刷新到 中央緩存 中,因此它們是空的。
當(dāng)一個(gè) goroutine 抓取一個(gè)新的 span 到 mcache 時(shí), goroutine 會(huì)清除 mcache 。
當(dāng) goroutine 顯式釋放對象或設(shè)置 finalizer 時(shí),goroutine 確保 span 已經(jīng)清除(通過清除或者等待并發(fā)清除完成)。
finalizer goroutine 僅在所有 span 已經(jīng)清除時(shí)才開始。
當(dāng)下一次 GC 啟動(dòng)時(shí),它將清除所有尚未清除的 span (如果有的話)。
下一次 GC 是在我們分配了與已經(jīng)使用的內(nèi)存成正比的額外內(nèi)存量之后。
該比例由 GOGC 環(huán)境變量控制(默認(rèn)為 100 )。
如果 GOGC=100 ,而我們使用的是 4M ,那么當(dāng)達(dá)到 8M 時(shí),我們將再次進(jìn)行 GC(此標(biāo)記在 next_gc 變量中被跟蹤)。
獲取 GOGC :
這使得 GC成本 與 allocation 成本 成線性比例。
調(diào)整 GOGC 只會(huì)改變線性常量(以及使用的額外內(nèi)存量)。
為了防止在掃描大型對象時(shí)出現(xiàn)長時(shí)間的暫停,并提高并行性,垃圾收集器將大于 maxObletBytes 的對象的掃描作業(yè)分解為最多 maxObletBytes 的 oblets 。
當(dāng)掃描遇到大對象時(shí),它只掃描第一個(gè) oblet ,并將其余 oblets 作為新的掃描作業(yè)排隊(duì)。
最近在看左神新書 《Go 語言設(shè)計(jì)與實(shí)現(xiàn)》的垃圾收集器時(shí)產(chǎn)生一個(gè)疑惑,花了點(diǎn)時(shí)間搞清楚了記錄一下。
Go 語言垃圾回收的實(shí)現(xiàn)使用了標(biāo)記清除算法,將對象的狀態(tài)抽象成黑色(活躍對象)、灰色(活躍對象中間狀態(tài))、白色(潛在垃圾對象也是所有對象的默認(rèn)狀態(tài))三種,注意沒有具體的字段標(biāo)記顏色。
整個(gè)標(biāo)記過程就是把白色對象標(biāo)黑的過程:
1.首先將 ROOT 根對象(包括全局變量、goroutine 棧上的對象等)放入到灰色集合
2.選一個(gè)灰色對象,標(biāo)成黑色,將所有可達(dá)的子對象放入到灰色集合
3.重復(fù)2的步驟,直到灰色集合中為空
下圖是書上的插圖,看上去是一個(gè)典型的深度優(yōu)先搜索的算法。
下圖是劉丹冰寫的《Golang 修養(yǎng)之路》的插圖,看上去是一個(gè)典型的廣度優(yōu)先搜索的算法。
我疑惑的點(diǎn)在于這個(gè)標(biāo)記過程是深度優(yōu)先算法還是廣度優(yōu)先算法,因?yàn)楹芏辔恼虏┛蛯Υ硕紱]有很清楚的說明,作為學(xué)習(xí)者這種細(xì)節(jié)其實(shí)也不影響對整個(gè) GC 流程的理解,但是這種細(xì)節(jié)我非常喜歡扣:)
對著書和源碼摸索著大致找到了一個(gè)結(jié)果是深度優(yōu)先。下面看下大致的過程,源碼基于1.15.2版本:
gcStart 是 Go 語言三種條件觸發(fā) GC 的共同入口
啟動(dòng)后臺(tái)標(biāo)記任務(wù)
為每個(gè)處理器創(chuàng)建用于執(zhí)行后臺(tái)標(biāo)記任務(wù)的 Goroutine
上面休眠的 G 會(huì)在調(diào)度循環(huán)中檢查并喚醒執(zhí)行
執(zhí)行標(biāo)記
gcw 是每個(gè) P 獨(dú)有的所以不用擔(dān)心并發(fā)的問題 和 GMP、mcache 一樣設(shè)計(jì),減少鎖競爭
嘗試在全局列表中獲取一個(gè)不為空的 buf
這是官方實(shí)現(xiàn)的無鎖隊(duì)列:)漲見識了,for 循環(huán)加原子操作實(shí)現(xiàn)棧的 pop
到這里從灰色集合中獲取待掃描的對象邏輯說完了。找到對象了接著就是 scanobject(b, gcw) 了,里面有兩段邏輯要注意
根據(jù)索引位置找到對象進(jìn)行標(biāo)色
嘗試存入 gcwork 的緩存中,或全局隊(duì)列中
無鎖隊(duì)列,for 循環(huán)加原子操作實(shí)現(xiàn)棧的 push
到這里把灰色對象標(biāo)黑就完成了,又放回灰色集合接著掃下一個(gè)指針。
Go 語言設(shè)計(jì)與實(shí)現(xiàn) 垃圾收集器
Golang三色標(biāo)記+混合寫屏障GC模式全分析
從Gov1.12版本開始,Go使用了非分代的、并發(fā)的、基于三色標(biāo)記清除的垃圾回收器。
關(guān)于垃圾回收,比較常見的算法有引用計(jì)數(shù)、標(biāo)記清除和分代收集,Golang語言使用的垃圾回收算法是標(biāo)記清除。
Golang語言的標(biāo)記清除垃圾回收算法,為了防止GC掃描時(shí)內(nèi)存變化引起的混亂。那么就需要 STW,即Stop The World。具體在Golang語言中是指,在GC時(shí)先停止所有g(shù)oroutine。再進(jìn)行垃圾回收,等待垃圾回收結(jié)束后再恢復(fù)所有被停止的goroutine。
標(biāo)記清除方法
啟動(dòng)STW,暫停程序的業(yè)務(wù)邏輯,找出不可達(dá)對象和可達(dá)對象。
將所有可達(dá)對象做標(biāo)記,清除未標(biāo)記的對象。停止STW,程序繼續(xù)執(zhí)行。循環(huán)往復(fù),直到進(jìn)程程序生命周期結(jié)束。因?yàn)镾TW需要暫停程序,為了減少暫停程序的時(shí)間。將清除操作移出 STW執(zhí)行周期,但是優(yōu)化效果不明顯。
所謂三色標(biāo)記,實(shí)際上只是為了方便敘述而抽象出來的一種說法,三色對應(yīng)垃圾回收過程中對象的三種狀態(tài)。白色是對象未被標(biāo)記,gcmarkBits對應(yīng)位為0,該對象將會(huì)在本次GC中被清理?;疑菍ο筮€在標(biāo)記隊(duì)列中等待被標(biāo)記,黑色是對象已被標(biāo)記,gcmarkBits對應(yīng)位為0,該對象將會(huì)在本次 GC中被回收。
網(wǎng)頁名稱:go語言gc狀態(tài)收集 go的gc對比java 的gc
路徑分享:http://jinyejixie.com/article8/dodoiip.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供動(dòng)態(tài)網(wǎng)站、企業(yè)建站、做網(wǎng)站、網(wǎng)站建設(shè)、服務(wù)器托管、關(guān)鍵詞優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)