Go 內(nèi)存管理 -- 垃圾回收

前言

go作為一個非常年輕的語言,吸取了各個語言的優(yōu)點,比如說Java中優(yōu)秀的垃圾回收,來釋放程序員一部分精力。
本篇要說的就是垃圾回收,常見的垃圾回收算法有標(biāo)記-清除、標(biāo)記整理、復(fù)制,然后在這些算法基礎(chǔ)上有分為分代&非分代回收,這些算法都非常優(yōu)秀,只是面對的場景不同罷了,但是要是想透徹的理解垃圾回收,看Java中的實現(xiàn)再合適不過了,如果能對于Java中的垃圾回收非常熟悉,理解go的垃圾回收將非常簡單。
go中的垃圾回收官方是這么描述的:非分代的、非緊縮的、寫屏障的并發(fā)標(biāo)記清除的垃圾回收。

標(biāo)記清除

標(biāo)記清除指的是對于那些已經(jīng)不會再使用的對象進行標(biāo)記,標(biāo)記完成后,對于標(biāo)記的對象進行清除。


image.png

很顯然如果使用標(biāo)記清除算法:
1、確定標(biāo)記的起點GCRoot
2、存在一定的內(nèi)存碎片
3、效率相對于復(fù)制、整理 效率要稍微高一些
但標(biāo)記清除是最常見的垃圾回收算法,Java 中CMS等垃圾回收器用的就是這個。
在標(biāo)記的過程中有一個所有垃圾回收算法(涉及GCRoot的初始標(biāo)記)都有的問題stop-the-world
標(biāo)記&清理的過程可以是串行的(效率很低),也可以是并發(fā)的。

三色標(biāo)記

三色標(biāo)記是一種在傳統(tǒng)的標(biāo)記清除算法基礎(chǔ)上衍生出來的一個改進的并發(fā)標(biāo)記算法:
1、首先創(chuàng)建三個集合:白、灰、黑
2、將所有對象放入白色集合中
3、然后從根節(jié)點開始遍歷所有對象(注意這里并不遞歸遍歷),把遍歷到的對象從白色集合放入灰色集合。
4、之后遍歷灰色集合,將灰色對象引用的對象從白色集合放入灰色集合,之后將此灰色對象放入黑色集合
5、重復(fù) 4 直到灰色中無任何對象
6、通過write-barrier檢測對象有變化,重復(fù)以上操作
7、收集所有白色對象(垃圾)


image.png

非常關(guān)鍵的一點是GCRoot的確定,這是整個算法的開端:
當(dāng)前goroutine的棧和全局?jǐn)?shù)據(jù)區(qū)中的對象作為GCRoot。

三色標(biāo)記中的并發(fā)標(biāo)記

所謂的并發(fā)標(biāo)記就是指在goroutine執(zhí)行的過程中能進行標(biāo)記行為,這里采用的方式與Java的CMS方式比較像,通過寫屏障來保證正確性。
比如說:當(dāng)從A這個GC root找到引用對象B時,B變灰A變黑。這時用戶goroutine執(zhí)行把A到B的引用改成了A到C的引用,同時B不再引用C。然后GC goroutine又執(zhí)行,發(fā)現(xiàn)B沒有引用對象,B變黑。而這時由于A已經(jīng)變黑完成了掃描,C將當(dāng)做白色不可達對象被清除,這里就會出現(xiàn)一個不該被清理的對象被清理了。
而寫屏障就是在這個出錯的地方做了下判斷:
當(dāng)發(fā)現(xiàn)A已經(jīng)標(biāo)記為黑色了,若A又引用C,那么把C變灰入隊。go gc時借助一個隊列,也就是gc-work來完成非遞歸遍歷。

強制回收

因為系統(tǒng)啟動或者短時間內(nèi)大量分配對象這些原因,會將垃圾回收的gc_trigger(垃圾回收的觸發(fā)器)的標(biāo)準(zhǔn)瞬間推高。當(dāng)服務(wù)正常后,活躍對象遠小于這個閾值,造成垃圾回收無法觸發(fā)。
所以需要有一個強制回收的觸發(fā),sysmon每隔2分鐘強制觸發(fā)GC一次。強制GC的goroutine一直park在后臺,直到sysmon將它喚醒開始執(zhí)行g(shù)c。

GC整體過程

Goff to Gmark
每次的gcstart都是滿足gc_triger時由mallocgc觸發(fā),整個的啟動過程是stop the world的,這個過程啟動了所有的GC工作協(xié)程,進入GCMark狀態(tài)使能寫屏障,啟動gcController。簡單來說就是確定GCroot相關(guān)的goroutine。
Gmark
這個階段是標(biāo)記階段,拿到準(zhǔn)備好的goroutine來做標(biāo)記,但是一開始就gopark當(dāng)前的goroutine(上個階段),直到被gccontroller的findRunnableGCWorker喚醒。
喚醒后進入標(biāo)記階段,每個worker都去gc-work中拿節(jié)點(節(jié)點置黑),然后處理當(dāng)前節(jié)點看有沒有指針和沒標(biāo)記的對象,繼續(xù)入隊子節(jié)點(灰化節(jié)點),直到隊列為空。
Gmarktermination
標(biāo)記結(jié)束后調(diào)用gcMarkDone
Gsweep
具體的清除行為,有多個時機可以出發(fā)Gsweep,如果是并發(fā)清除的話,需要先回收未被標(biāo)記的heap區(qū),然后喚醒進行sweep的 goroutine。
關(guān)于整體回收這一塊兒內(nèi)容,大家有興趣可以看一下源碼。
關(guān)于go的垃圾回收暫時就先介紹這么多。
關(guān)于go的內(nèi)存管理后續(xù)會單獨出一個系列,所以本系列僅僅闡述了一個內(nèi)存管理的梗概和基礎(chǔ)概念。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容