詳解Golang的GC三色標(biāo)記法
一 概念基礎(chǔ)
1.1三色標(biāo)記法將對(duì)象分為三類(lèi)
把圖過(guò)程中遇到的對(duì)象,按“是否訪(fǎng)問(wèn)過(guò)”這個(gè)條件標(biāo)記成以下三種顏色:
白色對(duì)象(可能死亡):未被回收器訪(fǎng)問(wèn)到的對(duì)象。在回收開(kāi)始階段,所有對(duì)象均為白色,當(dāng)回收結(jié)束后,白色對(duì)象均不可達(dá)。
灰色對(duì)象(波面):已被回收器訪(fǎng)問(wèn)到的對(duì)象,但回收器需要對(duì)其中的一個(gè)或多個(gè)指針進(jìn)行掃描,因?yàn)樗麄兛赡苓€指向白色對(duì)象。
黑色對(duì)象(確定存活):已被回收器訪(fǎng)問(wèn)到的對(duì)象,其中所有字段都已被掃描,黑色對(duì)象中任何一個(gè)指針都不可能直接指向白色對(duì)象。
1.2 標(biāo)記過(guò)程
- 起初所有的對(duì)象都是白色的;
- 從根對(duì)象出發(fā)掃描所有可達(dá)對(duì)象,標(biāo)記為灰色,放入待處理隊(duì)列;
- 從待處理隊(duì)列中取出灰色對(duì)象,將其引用的對(duì)象標(biāo)記為灰色并放入待處理隊(duì)列中,自身標(biāo)記為黑色;
- 重復(fù)步驟3,直到待處理隊(duì)列為空,此時(shí)白色對(duì)象即為不可達(dá)的“垃圾”,回收白色對(duì)象;
- 回收所有的白色對(duì)象,也就是回收垃圾
根對(duì)象
在垃圾回收的術(shù)語(yǔ)中又叫做根集合,它是垃圾回收器在標(biāo)記過(guò)程時(shí)最先檢查的對(duì)象。
- 全局變量:程序在編譯期就能確定的那些存在于程序整個(gè)生命周期的變量。
- 執(zhí)行棧:每個(gè) goroutine 都包含自己的執(zhí)行棧,這些執(zhí)行棧上包含棧上的變量及指向分配的堆內(nèi)存區(qū)塊的指針。
- 寄存器:寄存器的值可能表示一個(gè)指針,參與計(jì)算的這些指針可能指向某些賦值器分配的堆內(nèi)存區(qū)塊。
1.3 STW
STW(Stop The World)機(jī)制是指在進(jìn)行垃圾回收時(shí),會(huì)暫停應(yīng)用程序的運(yùn)行,以便進(jìn)行垃圾回收操作。這意味著在進(jìn)行垃圾回收時(shí),應(yīng)用程序?qū)o(wú)法繼續(xù)執(zhí)行。
為什么需要STW
STW(Stop-The-World)機(jī)制來(lái)確保并發(fā)操作的正確性。
如果不設(shè)置STW機(jī)制,那么在進(jìn)行GC時(shí),應(yīng)用程序線(xiàn)程可能會(huì)繼續(xù)執(zhí)行,從而導(dǎo)致內(nèi)存管理的不一致性和錯(cuò)誤。此外,GC可能會(huì)導(dǎo)致內(nèi)存分配和釋放的不連續(xù),從而導(dǎo)致內(nèi)存碎片化問(wèn)題。
因此,需要STW機(jī)制來(lái)確保GC的正確性和內(nèi)存管理的一致性。雖然STW機(jī)制會(huì)導(dǎo)致一定的性能損失,但是這是必要的代價(jià),以確保應(yīng)用程序的正確性和穩(wěn)定性。
1.4 屏障機(jī)制
1.4.1強(qiáng)、弱三色不變式
強(qiáng)三色不變式:強(qiáng)制性的不允許黑色對(duì)象引用白色對(duì)象,只能引用灰色對(duì)象,這樣就不會(huì)出現(xiàn)白色對(duì)象被誤刪的情況。
弱三色不等式 :
保護(hù)灰色對(duì)象到白色對(duì)象的路徑不會(huì)斷;
黑色對(duì)象可以引用白色對(duì)象,白色對(duì)象存在其他灰色對(duì)象對(duì)它的引用。
或可達(dá)它的鏈路上游存在灰色對(duì)象。這樣實(shí)則是黑色對(duì)象引用白色對(duì)象,白色對(duì)象處于一個(gè)被刪除的狀態(tài),但是上游灰色對(duì)象的引用,可以保護(hù)白色對(duì)象,使其安全。
為了遵循上述兩種方式,GC算法演進(jìn)到兩種屏障方式,“插入屏障”和“刪除屏障”。
1.4.2 插入屏障
在A對(duì)象引用B對(duì)象時(shí),B對(duì)象被標(biāo)記為灰色。(將B掛在A下游,B必須被標(biāo)記為灰色)
滿(mǎn)足強(qiáng)三色不等式。
插入屏障機(jī)制在??臻g的對(duì)象操作不使用,僅僅使用在堆空間對(duì)象的操作中。
1.4.3 刪除屏障
被刪除的對(duì)象,如果本身為灰色或白色,那么標(biāo)記為灰色。
滿(mǎn)足弱三色不等式。
1.4.4 混合屏障
插入寫(xiě)屏障和刪除寫(xiě)屏障的缺點(diǎn):
- 插入寫(xiě)屏障:結(jié)束時(shí)需要STW來(lái)重新掃描棧,標(biāo)記棧上引用的白色對(duì)象存活
- 刪除寫(xiě)屏障:回收精度低,GC開(kāi)始時(shí)STW掃描堆棧來(lái)記錄快照,這個(gè)過(guò)程會(huì)保護(hù)開(kāi)始時(shí)刻的所有的存活對(duì)象。
Go1.8引入混合寫(xiě)屏障機(jī)制,避免了對(duì)棧的重復(fù)掃描過(guò)程,極大減少了STW的時(shí)間。
- GC開(kāi)始將棧上的對(duì)象全部掃描并標(biāo)記為黑色(之后不再進(jìn)行第二次重復(fù)掃描,無(wú)需STW)
- GC期間,任何在棧上創(chuàng)建的新對(duì)象,都標(biāo)記為黑色
- 被刪除的對(duì)象標(biāo)記為灰色
- 被添加的對(duì)象標(biāo)記為灰色
二 GC過(guò)程
2.1 階段1:Mark Setup 標(biāo)記準(zhǔn)備
為了打開(kāi)寫(xiě)屏障,必須停止每個(gè)goroutine,讓垃圾收集器觀(guān)察并等待每個(gè)goroutine進(jìn)行函數(shù)調(diào)用,等待函數(shù)調(diào)用是為了保證goroutine停止時(shí)處于安全點(diǎn)。(期間會(huì)STW)
2.2 階段2:Marking 標(biāo)記
一旦寫(xiě)屏障打開(kāi),垃圾收集器就開(kāi)始標(biāo)記階段。
標(biāo)記階段需要標(biāo)記在堆內(nèi)存中仍然在使用中的值。首先檢查所有現(xiàn)goroutine的堆棧,以找到堆內(nèi)存的根指針。然后收集器必須從那些根指針遍歷堆內(nèi)存圖,標(biāo)記可以回收的內(nèi)存。
當(dāng)存在新的內(nèi)存分配時(shí),會(huì)暫停分配內(nèi)存過(guò)快的那些 goroutine,并將其轉(zhuǎn)去執(zhí)行一些輔助標(biāo)記(Mark Assist)的工作,從而達(dá)到放緩繼續(xù)分配、輔助 GC 的標(biāo)記工作的目的。
2.3階段3:Mark Termination 標(biāo)記結(jié)束
這個(gè)階段會(huì)關(guān)閉掉階段1開(kāi)啟的屏障,并計(jì)算下一次清理的目標(biāo)和計(jì)劃。(本階段會(huì)STW)
2.4 階段4:Sweeping 清理
清理階段用于回收標(biāo)記階段中標(biāo)記出來(lái)的可回收內(nèi)存。當(dāng)應(yīng)用程序goroutine嘗試在堆內(nèi)存中分配新內(nèi)存時(shí),會(huì)觸發(fā)該操作,清理導(dǎo)致的延遲和吞吐量降低被分散到每次內(nèi)存分配時(shí)。
本階段會(huì)并發(fā)執(zhí)行,清除前面標(biāo)記出來(lái)需清理的內(nèi)存。
以上就是詳解Golang的GC三色標(biāo)記法的詳細(xì)內(nèi)容,更多關(guān)于Golang GC 三色標(biāo)記法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
一文帶你了解Go語(yǔ)言實(shí)現(xiàn)的并發(fā)神庫(kù)conc
前幾天逛github發(fā)現(xiàn)了一個(gè)有趣的并發(fā)庫(kù)-conc,這篇文章將為大家詳細(xì)介紹一下這個(gè)庫(kù)的實(shí)現(xiàn),文中的示例代碼講解詳細(xì),感興趣的可以了解一下2023-01-01輕松入門(mén):使用Golang開(kāi)發(fā)跨平臺(tái)GUI應(yīng)用
Golang是一種強(qiáng)大的編程語(yǔ)言,它的并發(fā)性和高性能使其成為開(kāi)發(fā)GUI桌面應(yīng)用的理想選擇,Golang提供了豐富的標(biāo)準(zhǔn)庫(kù)和第三方庫(kù),可以輕松地創(chuàng)建跨平臺(tái)的GUI應(yīng)用程序,通過(guò)使用Golang的GUI庫(kù),開(kāi)發(fā)人員可以快速構(gòu)建具有豐富用戶(hù)界面和交互功能的應(yīng)用程序,需要的朋友可以參考下2023-10-10Go語(yǔ)言并發(fā)之context標(biāo)準(zhǔn)庫(kù)的使用詳解
Context的出現(xiàn)是為了解決在大型應(yīng)用程序中的并發(fā)環(huán)境下,協(xié)調(diào)和管理多個(gè)goroutine之間的通信、超時(shí)和取消操作的問(wèn)題,本文就來(lái)和大家簡(jiǎn)單聊聊它的具體用法,希望對(duì)大家有所幫助2023-06-06通過(guò)案例簡(jiǎn)單聊聊為什么說(shuō)Go中的字符串是不能被修改的
在接觸Go這么語(yǔ)言,可能你經(jīng)常會(huì)聽(tīng)到這樣一句話(huà),對(duì)于字符串不能修改,可能你很納悶,日常開(kāi)發(fā)中我們對(duì)字符串進(jìn)行修改也是很正常的,為什么又說(shuō)Go中的字符串不能進(jìn)行修改呢,本文就來(lái)通過(guò)實(shí)際案例給大家演示,為什么Go中的字符串不能進(jìn)行修改2023-07-07一文詳解Golang中字符串的常見(jiàn)錯(cuò)誤
這篇文章主要來(lái)和大家深入討論一下Golang?中的字符串,并查看一些不同的場(chǎng)景,以避免常見(jiàn)錯(cuò)誤,對(duì)大家掌握golang有一定的幫助,需要的可以了解下2023-10-10Golang的Fork/Join實(shí)現(xiàn)代碼
Fork/Join本質(zhì)上是一種任務(wù)分解,將一個(gè)很大的任務(wù)分解成若干個(gè)小任務(wù),然后再對(duì)小任務(wù)進(jìn)一步分解,直到最小顆粒度,然后并發(fā)執(zhí)行,對(duì)Golang的Fork/Join實(shí)現(xiàn)代碼感興趣的朋友跟隨小編一起看看吧2023-01-01