請(qǐng)先閱讀 golang的goroutine調(diào)度機(jī)制
golang的垃圾回收采用的是 標(biāo)記-清理(Mark-and-Sweep) 算法
就是先標(biāo)記出需要回收的內(nèi)存對(duì)象快苇倡,然后在清理掉;
在這里不介紹標(biāo)記和清理的具體策略,只介紹 GC過(guò)程是怎么調(diào)度的以及stw相關(guān)
這個(gè)算法陨帆,會(huì)導(dǎo)致 stw (stop the world) 的問(wèn)題锈颗,中斷用戶邏輯
觸發(fā)GC機(jī)制
在申請(qǐng)內(nèi)存的時(shí)候,檢查當(dāng)前當(dāng)前已分配的內(nèi)存是否大于上次GC后的內(nèi)存的2倍炼团,若是則觸發(fā)(主GC線程為當(dāng)前M)
監(jiān)控線程發(fā)現(xiàn)上次GC的時(shí)間已經(jīng)超過(guò)兩分鐘了澎嚣,觸發(fā);將一個(gè)G任務(wù)放到全局G隊(duì)列中去瘟芝。(主GC線程為執(zhí)行這個(gè)G任務(wù)的M)
每當(dāng)觸發(fā)的時(shí)候易桃,在主GC線程中就會(huì)走如下的GC流程:
stop the world,等待所有的M休眠模狭;此時(shí)所有的業(yè)務(wù)邏輯代碼都停止
標(biāo)記:分配gc標(biāo)記任務(wù)颈抚,喚醒 gcproc個(gè) M(就是第一步休眠的那些),分別做這個(gè)嚼鹉,直到所有的M都做完贩汉,才結(jié)束;并且所有M再次進(jìn)入休眠
清理:有一個(gè)單獨(dú)的goroutine去清理已經(jīng)標(biāo)記的內(nèi)存對(duì)象快
start the world锚赤,設(shè)置gcwaiting=0匹舞,喚醒所有的M(不會(huì)超過(guò)P個(gè)數(shù))
對(duì)于上面的三個(gè)步驟,分別解釋:
Stop the world:
設(shè)置gcwaiting=1线脚,這個(gè)在每一個(gè)G任務(wù)之前會(huì)檢查一次這個(gè)狀態(tài)赐稽,如是,則會(huì)將當(dāng)前M 休眠浑侥;
如果這個(gè)M里面正在運(yùn)行一個(gè)長(zhǎng)時(shí)間的G任務(wù)姊舵,咋辦呢,難道會(huì)等待這個(gè)G任務(wù)自己切換嗎寓落?這樣的話可要等10ms啊括丁,不能等!堅(jiān)決不能等伶选! 所以會(huì)主動(dòng)發(fā)出搶占標(biāo)記(類似于上一篇)史飞,讓當(dāng)前G任務(wù)中斷尖昏,再運(yùn)行下一個(gè)G任務(wù)的時(shí)候,就會(huì)走到第1步
一直等待所有的M進(jìn)入休眠构资,此時(shí)所有的業(yè)務(wù)邏輯代碼都停止
標(biāo)記:
根據(jù)gcproc的個(gè)數(shù)抽诉,分配成gcproc任務(wù)段;喚醒gcproc-1 個(gè)M來(lái)執(zhí)行(當(dāng)前M也算一個(gè))
對(duì)于一個(gè)M吐绵,喚醒前設(shè)置它的helpgc標(biāo)記迹淌,喚醒之后這個(gè)M會(huì)立馬判斷這個(gè)標(biāo)記,如是拦赠,則開始做分配給自己的標(biāo)記任務(wù)巍沙,如果先做完了,就會(huì)從別的M里面找一些來(lái)做
等每一個(gè)M都做完荷鼠,會(huì)再次進(jìn)入休眠
清理:
通過(guò)設(shè)置參數(shù)句携,可以以一個(gè)單獨(dú)goroutine 運(yùn)行,這個(gè)功能是在1.3版本之后增加的允乐,這樣的話就直接到下一步了矮嫉,清理過(guò)程不是stw的
也可以串行的在主GC線程執(zhí)行;這樣的話則清理過(guò)程也是stw的牍疏,
Start the world:
設(shè)置gcwaiting=0
喚醒P個(gè)M來(lái)繼續(xù)做G任務(wù)(此時(shí)沒有helpgc標(biāo)記)蠢笋,業(yè)務(wù)邏輯代碼開始
綜上:
是基于1.4 版本的,GC過(guò)程在標(biāo)記過(guò)程是(STW)的
在1.5 版本里面對(duì)GC做了很大的優(yōu)化鳞陨;采用三色標(biāo)記昨寞,將標(biāo)記過(guò)程細(xì)化成三段,只有前后的兩段是stw的厦滤;極大地縮短了gc的stw時(shí)間
感謝作者:liangzhiyang
查看原文:golang的垃圾回收(GC)機(jī)制