溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

golang中的gc原理是什么

發(fā)布時(shí)間:2022-01-18 10:13:23 來源:億速云 閱讀:136 作者:iii 欄目:數(shù)據(jù)庫

今天小編給大家分享一下golang中的gc原理是什么的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

gc出現(xiàn)

對于任何使用C語言的人,如果問他們C語言的最大煩惱是什么,其中許多人可能會(huì)回答說是指針和內(nèi)存泄漏。以致于后出現(xiàn)的語言,都在幫助程序員來處理內(nèi)存泄漏的問題,比較有名的語言java、python、go等等,都有一個(gè)比較重要的機(jī)制,那就是gc(Garbage Collection),也就是垃圾收集器。當(dāng)然這也是得意于這些語言的特質(zhì),它們在運(yùn)行的時(shí)候,都有一個(gè)諸如golang中runtime的機(jī)制,java中有jvm來管理。程序員不再需要考慮,我什么時(shí)候應(yīng)該分配內(nèi)存,什么時(shí)候應(yīng)該回收內(nèi)存。如果是從c語言開始學(xué)習(xí)編程的,應(yīng)該對malloc和free非常熟悉,再寫代碼的時(shí)候回小心翼翼的使用這兩個(gè)函數(shù)。如果是學(xué)python的,根本就不會(huì)注意到這個(gè)問題,有不會(huì)關(guān)心這個(gè)方面的問題。比較值得說明的是,在c語言中使用char列表來存儲(chǔ)字符串,是一件非常麻煩的事情,在java等但是,就算如此,在日常的寫程序的過程中,程序員也應(yīng)該注意gc方面的問題。一個(gè)比較差的設(shè)計(jì),可能使得gc對于整個(gè)程序的負(fù)擔(dān)非常的大,可能會(huì)發(fā)現(xiàn)程序中g(shù)c的時(shí)間占比非常高。

gc的原理

gc常見的方式有:

引用計(jì)數(shù)(reference counting)每個(gè)對象維護(hù)一個(gè)引用計(jì)數(shù)器,當(dāng)引用該對象的對象被銷毀或者更新的時(shí)候,被引用對象的引用計(jì)數(shù)器自動(dòng)減 1,當(dāng)被應(yīng)用的對象被創(chuàng)建,或者賦值給其他對象時(shí),引用 +1,引用為 0 的時(shí)候回收,思路簡單,但是頻繁更新引用計(jì)數(shù)器降低性能,存在循環(huán)以引用(php,Python所使用的)

標(biāo)記清除(mark and sweep)就是 golang 所使用的,從根變量來時(shí)遍歷所有被引用對象,標(biāo)記之后進(jìn)行清除操作,對未標(biāo)記對象進(jìn)行回收,缺點(diǎn):每次垃圾回收的時(shí)候都會(huì)暫停所有的正常運(yùn)行的代碼,系統(tǒng)的響應(yīng)能力會(huì)大大降低,各種 mark&swamp 變種(三色標(biāo)記法),緩解性能問題。

分代搜集(generation)jvm 就使用的分代回收的思路。在面向?qū)ο?span id="t11u6ca" class="wp_keywordlink">編程語言中,絕大多數(shù)對象的生命周期都非常短。分代收集的基本思想是,將堆劃分為兩個(gè)或多個(gè)稱為代(generation)的空間。新創(chuàng)建的對象存放在稱為新生代(young generation)中(一般來說,新生代的大小會(huì)比 老年代小很多),隨著垃圾回收的重復(fù)執(zhí)行,生命周期較長的對象會(huì)被提升(promotion)到老年代中(這里用到了一個(gè)分類的思路,這個(gè)是也是科學(xué)思考的一個(gè)基本思路)。

golang中的gc原理

go1.3以前gc最大的問題在于stw(stop the word),即在gc的時(shí)候需要暫停程序行為,然后進(jìn)標(biāo)記,最后將未標(biāo)記的垃圾清除。如果頻繁的觸發(fā)gc的話,程序的運(yùn)行就一卡一卡的。其基本的思路就是:

1.標(biāo)記:在內(nèi)存堆中(由于有的時(shí)候管理內(nèi)存頁的時(shí)候要用到堆的數(shù)據(jù)結(jié)構(gòu),所以稱為堆內(nèi)存)存儲(chǔ)著有一系列的對象,這些對象可能會(huì)與其他對象有關(guān)聯(lián)(references between these objects) a tracing garbage collector 會(huì)在某一個(gè)時(shí)間點(diǎn)上停止原本正在運(yùn)行的程序,之后它會(huì)掃描 runtim e已經(jīng)知道的的 object 集合(already known set of objects),通常它們是存在于 stack 中的全局變量以及各種對象。gc 會(huì)對這些對象進(jìn)行標(biāo)記,將這些對象的狀態(tài)標(biāo)記為可達(dá),從中找出所有的,從當(dāng)前的這些對象可以達(dá)到其他地方的對象的 reference,并且將這些對象也標(biāo)記為可達(dá)的對象,這個(gè)步驟被稱為 mark phase,即標(biāo)記階段,這一步的主要目的是用于獲取這些對象的狀態(tài)信息。

2.回收:一旦將所有的這些對象都掃描完,gc 就會(huì)獲取到所有的無法 reach 的對象(狀態(tài)為 unreachable 的對象),并且將它們回收,這一步稱為 sweep phase,即是清掃階段。

3.清除:gc 僅僅搜集那些未被標(biāo)記為可達(dá)(reachable)的對象。如果 gc 沒有識(shí)別出一個(gè) reference,最后有可能會(huì)將一個(gè)仍然在使用的對象給回收掉,就引起了程序運(yùn)行錯(cuò)誤。

go在1.3的時(shí)候引入了并發(fā)清理,go team 自己的說法是減少了 50%-70% 的暫停時(shí)間。
go在1.5時(shí)候使用了三色標(biāo)記法,這個(gè)是標(biāo)記清除算法的一個(gè)升級(jí)變種。流程如下:

1.灰色:對象已被標(biāo)記,但這個(gè)對象包含的子對象未標(biāo)記

2.黑色:對象已被標(biāo)記,且這個(gè)對象包含的子對象也已標(biāo)記,gcmarkBits對應(yīng)的位為1(該對象不會(huì)在本次GC中被清理)

3.白色:對象未被標(biāo)記,gcmarkBits對應(yīng)的位為0(該對象將會(huì)在本次GC中被清理)

例如,當(dāng)前內(nèi)存中有A~F一共6個(gè)對象,根對象a,b本身為棧上分配的局部變量,根對象a、b分別引用了對象A、B, 而B對象又引用了對象D,則GC開始前各對象的狀態(tài)如下圖所示:

1.初始狀態(tài)下所有對象都是白色的。

2.接著開始掃描根對象a、b; 由于根對象引用了對象A、B,那么A、B變?yōu)榛疑珜ο螅酉聛砭烷_始分析灰色對象,分析A時(shí),A沒有引用其他對象很快就轉(zhuǎn)入黑色,B引用了D,則B轉(zhuǎn)入黑色的同時(shí)還需要將D轉(zhuǎn)為灰色,進(jìn)行接下來的分析。

3.灰色對象只有D,由于D沒有引用其他對象,所以D轉(zhuǎn)入黑色。標(biāo)記過程結(jié)束

4.最終,黑色的對象會(huì)被保留下來,白色對象會(huì)被回收掉。

go中的gc過程

GO的GC是并行GC, 也就是GC的大部分處理和普通的go代碼是同時(shí)運(yùn)行的, 這讓GO的GC流程比較復(fù)雜。

1.Stack scan:Collect pointers from globals and goroutine stacks。收集根對象(全局變量,和G stack),開啟寫屏障。全局變量、開啟寫屏障需要STW,G stack只需要停止該G就好,時(shí)間比較少。

2.Mark: Mark objects and follow pointers。標(biāo)記所有根對象, 和根對象可以到達(dá)的所有對象不被回收。

3.Mark Termination: Rescan globals/changed stack, finish mark。重新掃描全局變量,和上一輪改變的stack(寫屏障),完成標(biāo)記工作。這個(gè)過程需要STW。

4.Sweep: 按標(biāo)記結(jié)果清掃span

從1.8以后的golang將第一步的stop the world 也取消了,這又是一次優(yōu)化;1.9開始, 寫屏障的實(shí)現(xiàn)使用了Hybrid Write Barrier, 大幅減少了第二次STW的時(shí)間.
因?yàn)間o支持并行GC, GC的掃描和go代碼可以同時(shí)運(yùn)行, 這樣帶來的問題是GC掃描的過程中g(shù)o代碼有可能改變了對象的依賴樹。
例如開始掃描時(shí)發(fā)現(xiàn)根對象A和B, B擁有C的指針。

1.GC先掃描A,A放入黑色

2.B把C的指針交給A

3.GC再掃描B,B放入黑色

4.C在白色,會(huì)回收;但是A其實(shí)引用了C。

為了避免這個(gè)問題, go在GC的標(biāo)記階段會(huì)啟用寫屏障(Write Barrier)。啟用了寫屏障(Write Barrier)后,在GC第三輪rescan階段,根據(jù)寫屏障標(biāo)記將C放入灰色,防止C丟失。

以上就是“golang中的gc原理是什么”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI