Golang 1.14中內(nèi)存分配屿笼、清掃和內(nèi)存回收

golang內(nèi)存分配

Golang的內(nèi)存分配是由golang runtime完成丧叽,其內(nèi)存分配方案借鑒自tcmalloc眷柔。
主要特點(diǎn)就是

  • 為每個(gè)工作線程(P)都維護(hù)了一個(gè)分配cache期虾,小對(duì)象((16,32KB])和微(Tiny)對(duì)象((1B,16B])基本上無需全局鎖驯嘱。
  • 為常用的內(nèi)存尺寸(16B镶苞,32KB] 之間內(nèi)存分類為numSpanClass(134-2)種對(duì)象尺寸。位于此大小區(qū)間的內(nèi)存分配都?xì)w到這些spanclass 尺寸的element上進(jìn)行分配鞠评。如此可以減少內(nèi)存碎片的產(chǎn)生茂蚓。

本文中的element指一定大小的內(nèi)存塊是內(nèi)存分配的概念,并為出現(xiàn)在golang runtime源碼中
本文講述x8664架構(gòu)下的內(nèi)存分配

主要結(jié)構(gòu)

Golang 內(nèi)存分配有下面幾個(gè)主要結(jié)構(gòu)

  • mspan 用于管理一連串地址連續(xù)的頁面剃幌,golang將同一mspan維護(hù)的地址空間劃分為同一個(gè)尺寸的element聋涨,每個(gè)element用于存儲(chǔ)一個(gè)大小屬于當(dāng)前SpanClass對(duì)象。
  • mcache 每個(gè)P都有一個(gè)mcache负乡,在此mcache上有一個(gè)長度為numSpanClass的數(shù)組牍白,里面存儲(chǔ)的每個(gè)成員指向一個(gè)mspan。
  • mcentral 全局的抖棘,每個(gè)mcentral用于管理相同的element size的mspan茂腥。
  • mheap 全局的狸涌,用于管理golang的整個(gè)堆。在mheap上有numSpanClass個(gè)mcentral數(shù)組础芍。
  • heapArena 在x8664上用于管理64MB的堆空間杈抢。一個(gè)heapArena下有多個(gè)mspan数尿。


    圖片發(fā)自簡書App

內(nèi)存分配方案

微(Tiny)對(duì)象

Tiny對(duì)象是指內(nèi)存尺寸小于16B的對(duì)象仑性,這類對(duì)象的分配使用mcache的tiny區(qū)域進(jìn)行分配。當(dāng)tiny區(qū)域空間耗盡時(shí)刻右蹦,它會(huì)從mcache.alloc[tinySpanClass]指向的mspan中找到空閑的區(qū)域诊杆。當(dāng)然如果mcache中span空間也耗盡,它會(huì)觸發(fā)從mcentral補(bǔ)充mspan到mcache的流程何陆。

小對(duì)象

小對(duì)象是指對(duì)象尺寸在(16B晨汹,32KB]之間的對(duì)象,這類對(duì)象的分配原則是:
1贷盲、首先根據(jù)對(duì)象尺寸將對(duì)象歸為某個(gè)SpanClass上淘这,這個(gè)SpanClass上所有的element都是一個(gè)統(tǒng)一的尺寸。
2巩剖、從mcache.alloc[SpanClass]找到mspan铝穷,看看有無空閑的element,如果有分配成功佳魔。如果沒有繼續(xù)曙聂。
3、從mcentral.allocSpan[SpanClass]的nonempty和emtpy中找到合適的mspan鞠鲜,返回給mcache。如果沒有找到就進(jìn)入mcentral.grow()—>mheap.alloc()分配新的mspan給mcentral。

大對(duì)象的分配

大對(duì)象指尺寸超出32KB的對(duì)象源祈,此時(shí)直接從mheap中分配猛拴,不會(huì)走mcache和mcentral,直接走mheap.alloc()分配一個(gè)SpanClass==0 的mspan表示這部分分配空間霞捡。

總結(jié)

對(duì)于程序分配常用的tiny和小對(duì)象的分配语稠,可以通過無鎖的mcache提升分配性能。mcache不足時(shí)刻會(huì)拿mcentral的鎖弄砍,然后從mcentral中充mspan 給mcache仙畦。大對(duì)象直接從mheap 中分配。

進(jìn)程虛擬地址空間管理

在x8664環(huán)境上音婶,golang管理的有效的程序虛擬地址空間實(shí)質(zhì)上只有48位慨畸。在mheap中有一個(gè)pages pageAlloc成員用于管理golang堆內(nèi)存的地址空間。golang從os中申請(qǐng)地址空間給自己管理衣式,地址空間申請(qǐng)下來以后寸士,golang會(huì)將地址空間根據(jù)實(shí)際使用情況標(biāo)記為free或者alloc檐什。如果地址空間被分配給mspan或大對(duì)象后,那么被標(biāo)記為alloc弱卡,反之就是free乃正。

地址空間的狀態(tài)

Golang認(rèn)為地址空間有以下4種狀態(tài):

  • None 地址空間初始狀態(tài)
  • Reserved 地址已經(jīng)被golang runtime擁有,但是os并為真正分配婶博,訪問此類地址出發(fā)異常
  • Prepared 地址已經(jīng)是Reserved瓮具,但是也并為被OS分配真正地址空間。在Linux系統(tǒng)上凡人,Prepared對(duì)應(yīng)的是地址空間為MADV_FREE 表示os可以在自己認(rèn)為需要的時(shí)候回收這段地址空間名党。
  • Ready 地址空間真真正正被os分配。訪問此空間不會(huì)出發(fā)異常挠轴。

Golang同時(shí)定義了下面幾個(gè)地址空間操作函數(shù):

  • sysReserved 調(diào)用此函數(shù)后传睹,地址從None轉(zhuǎn)換為Reserved狀態(tài)
  • sysAlloc 地址從None轉(zhuǎn)換為Ready狀態(tài),一般都是golang runtime自己內(nèi)存管理對(duì)象的分配使用sysAlloc
  • sysFree 地址空間從任意狀態(tài)轉(zhuǎn)換為None
  • sysMap 地址空間從Reserved轉(zhuǎn)換為Prepared
  • sysUsed 地址空間從Prepared轉(zhuǎn)換為Ready
  • sysUnused 地址空間從Ready轉(zhuǎn)換為Prepared


golang如何管理自己的虛擬地址空間

在mheap結(jié)構(gòu)中岸晦,有一個(gè)名為pages成員欧啤,它用于golang 堆使用虛擬地址空間進(jìn)行管理。其類型為pageAlloc

type pageAlloc struct{
...
summary [summacryLevels][]pallocSum
chunks [1<<pallocChunksL1Bits][1<<pallocChunksL2Bits]
...
}
type pallocSum uint64
type pallocData struct{
     pallocBits
     scavenged pageBits
}
type pallocBits pageBits
// pallocChunkPages/64 =256/64 =4
type pageBits [pallocChunkPages/64]uint64

pageAlloc 結(jié)構(gòu)表示的golang 堆的所有地址空間启上。其中最重要的成員有兩個(gè):

  • summary 為二維數(shù)組邢隧,組成一個(gè)pallocSum的radix tree。在golang 1.14中x8664 radix tree 為4級(jí)碧绞。最終那一級(jí)節(jié)點(diǎn)表示一個(gè)chunk(2MB)地址空間的分配情況府框。
    pallocSum定義為64bit 長整型,它被分為三個(gè)bitmap:start讥邻,max 和end迫靖。start表示這段地址空間從起始地址開始連續(xù)的free的地址空間的頁面數(shù)量;max這段地址空間最大連續(xù)頁的數(shù)量兴使,end 為這段地址空間的最后一個(gè)頁編號(hào)系宜。


  • chunks 數(shù)組每個(gè)成員表示了地址空間內(nèi)所有chunk里頁面分配和scavenge情況。其結(jié)構(gòu)為pallocData发魄,分為兩個(gè)成員盹牧,pallocBits成員表示已分配頁面的bitmap,scavenged 成員表示已scavenged的頁面的bitmap励幼。
    chunk如前所述在x8664上為2MB汰寓,也就是常規(guī)的一個(gè)巨頁頁面大小
    當(dāng)alloc mspan時(shí)刻,需要使用pageAlloc苹粟,涉及到下面函數(shù)的使用:

  • (s* pageAlloc)alloc找到一個(gè)地址空間可以容納待分配的頁面數(shù)量有滑,并把這段地址空間標(biāo)記為alloc。

  • (s*pageAlloc)grow 增長mheap管理的堆地址空間

  • (s*pageAlloc)free 釋放地址空間(將這段地址空間標(biāo)記為free)
    alloc mspan時(shí)嵌削,先使用pageAalloc.alloc嘗試分配一段地址空間毛好,如果沒有分配成功望艺,就使用grow增加一段地址空間映射,最后再使用alloc分配肌访。

空間清掃sweep

在golang的gc流程中會(huì)將未使用的對(duì)象標(biāo)記為未使用找默,但是這些對(duì)象所使用的地址空間并未交還給os。地址空間的申請(qǐng)和釋放都是以golang的page為單位(實(shí)際以chunk為單位)進(jìn)行的吼驶。sweep的最終結(jié)果只是將某個(gè)地址空間標(biāo)記可被分配惩激,并未真正釋放地址空間給os,真正釋放是后文的scavenge過程旨剥。

mspan的sweep

在gc mark結(jié)束以后會(huì)使用sweep()去嘗試free一個(gè)span咧欣;在mheap.alloc 申請(qǐng)mspan時(shí)刻浅缸,也使用sweep去清掃一下轨帜。
清掃mspan主要涉及到下面函數(shù)

  • mspan.sweep()掃描這個(gè)span中element的使用情況,當(dāng)最終如果整個(gè)mspan所有的element都釋放了衩椒,那么使用freeSpan()
  • mheap.freeSpan()釋放golang runtime的這個(gè)mspan對(duì)象蚌父,同時(shí)將mspan表示的地址空間標(biāo)記為可分配。

地址空間回收(scavenge)

如上節(jié)所述毛萌,sweep只是將page標(biāo)記為可分配苟弛,但是并未把地址空間釋放;真正的地址空間釋放是scavenge過程阁将。
真正的scavenge是由pageAlloc.scavenge()—>sysUnused()將掃描到待釋放的chunk所表示的地址空間釋放掉(使用sysUnused()將地址空間還給os)
golang的scavenge過程有兩種:

  • 同步scavenge膏秫,當(dāng)每次mheap.grow() 增長mheap的內(nèi)存的時(shí)刻,如果增長量達(dá)到一定水平就會(huì)觸發(fā)scavenge的掃描過程做盅。在scavenge掃描過程中缤削,golang會(huì)嘗試釋放一定數(shù)量的chunk。
  • 后臺(tái)scavenge吹榴,golang內(nèi)部有定時(shí)器和相關(guān)的goroutine亭敢,定期掃描程序內(nèi)存使用量,當(dāng)內(nèi)存使用量超出一定閾值的時(shí)候图筹,也會(huì)調(diào)用scavenge過程帅刀,嘗試釋放內(nèi)存給os系統(tǒng)。
  • golang runtime package中定義了 debug.freeOSMemory 會(huì)手動(dòng)觸發(fā)一次gc远剩,并調(diào)用scavengeAll扣溺,對(duì)mheap管理所有地址空間進(jìn)行scavenge *
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瓜晤,隨后出現(xiàn)的幾起案子锥余,更是在濱河造成了極大的恐慌,老刑警劉巖活鹰,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哈恰,死亡現(xiàn)場離奇詭異只估,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)着绷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門蛔钙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人荠医,你說我怎么就攤上這事吁脱。” “怎么了彬向?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵兼贡,是天一觀的道長。 經(jīng)常有香客問我娃胆,道長遍希,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任里烦,我火速辦了婚禮凿蒜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘胁黑。我一直安慰自己废封,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布丧蘸。 她就那樣靜靜地躺著漂洋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪力喷。 梳的紋絲不亂的頭發(fā)上刽漂,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音冗懦,去河邊找鬼爽冕。 笑死,一個(gè)胖子當(dāng)著我的面吹牛披蕉,可吹牛的內(nèi)容都是我干的颈畸。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼没讲,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼眯娱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起爬凑,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤徙缴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體于样,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡疏叨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了穿剖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚤蔓。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖糊余,靈堂內(nèi)的尸體忽然破棺而出秀又,到底是詐尸還是另有隱情,我是刑警寧澤贬芥,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布吐辙,位于F島的核電站,受9級(jí)特大地震影響蘸劈,放射性物質(zhì)發(fā)生泄漏昏苏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一昵时、第九天 我趴在偏房一處隱蔽的房頂上張望捷雕。 院中可真熱鬧椒丧,春花似錦壹甥、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至棒假,卻和暖如春溯职,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背帽哑。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國打工谜酒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人妻枕。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓僻族,卻偏偏與公主長得像,于是被迫代替她去往敵國和親屡谐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子述么,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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