圖解Go語言內(nèi)存分配_第1頁
圖解Go語言內(nèi)存分配_第2頁
圖解Go語言內(nèi)存分配_第3頁
圖解Go語言內(nèi)存分配_第4頁
圖解Go語言內(nèi)存分配_第5頁
已閱讀5頁,還剩3頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

圖解Go語?內(nèi)存分配Go語?內(nèi)置運(yùn)?時(shí)(就是runtime),拋棄了傳統(tǒng)的內(nèi)存分配?式,改為?主管理。這樣可以?主地實(shí)現(xiàn)更好的內(nèi)存使?模式,?如內(nèi)存池、預(yù)分配等等。這樣,不會(huì)每次內(nèi)存分配都需要進(jìn)?系統(tǒng)調(diào)?。Golang運(yùn)?時(shí)的內(nèi)存分配算法主要源?Google為C語?開發(fā)的TCMalloc算法,全稱Thread-CachingMalloc。核?思想就是把內(nèi)存分為多級(jí)管理,從?降低鎖的粒度。它將可?的堆內(nèi)存采??級(jí)分配的?式進(jìn)?管理:每個(gè)線程都會(huì)??維護(hù)?個(gè)獨(dú)?的內(nèi)存池,進(jìn)?內(nèi)存分配時(shí)優(yōu)先從該內(nèi)存池中分配,當(dāng)內(nèi)存池不?時(shí)才會(huì)向全局內(nèi)存池申請(qǐng),以避免不同線程對(duì)全局內(nèi)存池的頻繁競(jìng)爭(zhēng)。為了更好的閱讀體驗(yàn),?動(dòng)貼上?章?錄:概念Go在程序啟動(dòng)的時(shí)候,會(huì)先向操作系統(tǒng)申請(qǐng)?塊內(nèi)存(注意這時(shí)還只是?段虛擬的地址空間,并不會(huì)真正地分配內(nèi)存),切成?塊后??進(jìn)?管理。申請(qǐng)到的內(nèi)存塊被分配了三個(gè)區(qū)域,在X64上分別是512MB,16GB,512GB??。arena區(qū)域就是我們所謂的堆區(qū),Go動(dòng)態(tài)分配的內(nèi)存都是在這個(gè)區(qū)域,它把內(nèi)存分割成8KB??的頁,?些頁組合起來稱為mspan。bitmap區(qū)域標(biāo)識(shí)arena區(qū)域哪些地址保存了對(duì)象,并且?4bit標(biāo)志位表?對(duì)象是否包含指針、GC標(biāo)記信息。bitmap中?個(gè)byte??的內(nèi)存對(duì)應(yīng)arena區(qū)域中4個(gè)指針??(指針??為8B)的內(nèi)存,所以bitmap區(qū)域的??是512GB/(4*8B)=16GB。從上圖其實(shí)還可以看到bitmap的?地址部分指向arena區(qū)域的低地址部分,也就是說bitmap的地址是由?地址向低地址增長(zhǎng)的。spans區(qū)域存放mspan(也就是?些arena分割的頁組合起來的內(nèi)存管理基本單元,后?會(huì)再講)的指針,每個(gè)指針對(duì)應(yīng)?頁,所以spans區(qū)域的??就是512GB/8KB*8B=512MB。除以8KB是計(jì)算arena區(qū)域的頁數(shù),?最后乘以8是計(jì)算spans區(qū)域所有指針的??。創(chuàng)建mspan的時(shí)候,按頁填充對(duì)應(yīng)的spans區(qū)域,在回收object時(shí),根據(jù)地址很容易就能找到它所屬的mspan。內(nèi)存管理單元mspan:Go中內(nèi)存管理的基本單元,是由??連續(xù)的8KB的頁組成的?塊內(nèi)存。注意,這?的頁和操作系統(tǒng)本?的頁并不是?回事,它?般是操作系統(tǒng)頁??的?倍。?句話概括:mspan是?個(gè)包含起始地址、mspan規(guī)格、頁的數(shù)量等內(nèi)容的雙端鏈表。每個(gè)mspan按照它??的屬性SizeClass的??分割成若?個(gè)object,每個(gè)object可存儲(chǔ)?個(gè)對(duì)象。并且會(huì)使??個(gè)位圖來標(biāo)記其尚未使?的object。屬性SizeClass決定object??,?mspan只會(huì)分配給和object尺???接近的對(duì)象,當(dāng)然,對(duì)象的??要?于object??。還有?個(gè)概念:SpanClass,它和SizeClass的含義差不多,Size_Class=Span_Class/2這是因?yàn)槠鋵?shí)每個(gè)SizeClass有兩個(gè)mspan,也就是有兩個(gè)SpanClass。其中?個(gè)分配給含有指針的對(duì)象,另?個(gè)分配給不含有指針的對(duì)象。這會(huì)給垃圾回收機(jī)制帶來利好,之后的?章再談。如下圖,mspan由?組連續(xù)的頁組成,按照?定??劃分成object。Go1.9.2?mspan的SizeClass共有67種,每種mspan分割的object??是8*2n的倍數(shù),這個(gè)是寫死在代碼?的://path:/usr/local/go/src/runtime/sizeclasses.goconst_NumSizeClasses=67varclass_to_size=[_NumSizeClasses]uint16{,8,16,32,48,64,80,96,112,128,144,160,176,192,208,224,240,256,288,320,352,384,416,448,480,5根據(jù)mspan的SizeClass可以得到它劃分的object??。?如SizeClass等于3,object??就是32B。32B??的object可以存儲(chǔ)對(duì)象??范圍在17B~32B的對(duì)象。?對(duì)于微?對(duì)象(?于16B),分配器會(huì)將其進(jìn)?合并,將?個(gè)對(duì)象分配到同?個(gè)object中。數(shù)組?最?的數(shù)是32768,也就是32KB,超過此??就是?對(duì)象了,它會(huì)被特別對(duì)待,這個(gè)稍后會(huì)再介紹。順便提?句,類型SizeClass為0表??對(duì)象,它實(shí)際上直接由堆內(nèi)存分配,??對(duì)象都要通過mspan來分配。對(duì)于mspan來說,它的SizeClass會(huì)決定它所能分到的頁數(shù),這也是寫死在代碼?的://path:/usr/local/go/src/runtime/sizeclasses.goconst_NumSizeClasses=67varclass_to_allocnpages=[_NumSizeClasses]uint8{,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,2,1,2,1,3,2,3,?如當(dāng)我們要申請(qǐng)?個(gè)object??為32B的mspan的時(shí)候,在class_to_size?對(duì)應(yīng)的索引是3,?索引3在class_to_allocnpages數(shù)組?對(duì)應(yīng)的頁數(shù)就是1。mspan結(jié)構(gòu)體定義://path:/usr/local/go/src/runtime/mheap.gotypemspanstruct{//鏈表前向指針,?于將span鏈接起來next*mspan//鏈表前向指針,?于將span鏈接起來prev*mspan//起始地址,也即所管理頁的地址startAddruintptr//管理的頁數(shù)npagesuintptr//塊個(gè)數(shù),表?有多少個(gè)塊可供分配nelemsuintptr//分配位圖,每?位代表?個(gè)塊是否已分配allocBits*gcBits//已分配塊的個(gè)數(shù)allocCountuint16//class表中的classID,和SizeClasss相關(guān)spanclassspanClass//class表中的對(duì)象??,也即塊??elemsizeuintptr}我們將mspan放到更?的視?來看:上圖可以看到有兩個(gè)S指向了同?個(gè)mspan,因?yàn)檫@兩個(gè)S指向的P是同屬?個(gè)mspan的。所以,通過arena上的地址可以快速找到指向它的S,通過S就能找到mspan,回憶?下前?我們說的mspan區(qū)域的每個(gè)指針對(duì)應(yīng)?頁。假設(shè)最左邊第?個(gè)mspan的SizeClass等于10,根據(jù)前?的class_to_size數(shù)組,得出這個(gè)msapn分割的object??是144B,算出可分配的對(duì)象個(gè)數(shù)是8KB/144B=56.89個(gè),取整56個(gè),所以會(huì)有?些內(nèi)存浪費(fèi)掉了,Go的源碼?有所有SizeClass的mspan浪費(fèi)的內(nèi)存的??;再根據(jù)class_to_allocnpages數(shù)組,得到這個(gè)mspan只由1個(gè)page組成;假設(shè)這個(gè)mspan是分配給?指針對(duì)象的,那么spanClass等于20。startAddr直接指向arena區(qū)域的某個(gè)位置,表?這個(gè)mspan的起始地址,allocBits指向?個(gè)位圖,每位代表?個(gè)塊是否被分配了對(duì)象;allocCount則表?總共已分配的對(duì)象個(gè)數(shù)。這樣,左起第?個(gè)mspan的各個(gè)字段參數(shù)就如下圖所?:內(nèi)存管理組件內(nèi)存分配由內(nèi)存分配器完成。分配器由3種組件構(gòu)成:mcache,mcentral,mheap。mcachemcache:每個(gè)?作線程都會(huì)綁定?個(gè)mcache,本地緩存可?的mspan資源,這樣就可以直接給Goroutine分配,因?yàn)椴淮嬖诙鄠€(gè)Goroutine競(jìng)爭(zhēng)的情況,所以不會(huì)消耗鎖資源。mcache的結(jié)構(gòu)體定義://path:/usr/local/go/src/runtime/mcache.gotypemcachestruct{alloc[numSpanClasses]*mspan}numSpanClasses=_NumSizeClasses<<1mcache?SpanClasses作為索引管理多個(gè)?于分配的mspan,它包含所有規(guī)格的mspan。它是_NumSizeClasses的2倍,也就是67*2=134,為什么有?個(gè)兩倍的關(guān)系,前?我們提到過:為了加速之后內(nèi)存回收的速度,數(shù)組??半的mspan中分配的對(duì)象不包含指針,另?半則包含指針。對(duì)于?指針對(duì)象的mspan在進(jìn)?垃圾回收的時(shí)候?需進(jìn)?步掃描它是否引?了其他活躍的對(duì)象。后?的垃圾回收?章會(huì)再講到,這次先到這?。mcache在初始化的時(shí)候是沒有任何mspan資源的,在使?過程中會(huì)動(dòng)態(tài)地從mcentral申請(qǐng),之后會(huì)緩存下來。當(dāng)對(duì)象?于等于32KB??時(shí),使?mcache的相應(yīng)規(guī)格的mspan進(jìn)?分配。mcentralmcentral:為所有mcache提供切分好的mspan資源。每個(gè)central保存?種特定??的全局mspan列表,包括已分配出去的和未分配出去的。每個(gè)mcentral對(duì)應(yīng)?種mspan,?mspan的種類導(dǎo)致它分割的object??不同。當(dāng)?作線程的mcache中沒有合適(也就是特定??的)的mspan時(shí)就會(huì)從mcentral獲取。mcentral被所有的?作線程共同享有,存在多個(gè)Goroutine競(jìng)爭(zhēng)的情況,因此會(huì)消耗鎖資源。結(jié)構(gòu)體定義://path:/usr/local/go/src/runtime/mcentral.gotypemcentralstruct{//互斥鎖lockmutex//規(guī)格sizeclassint32//尚有空閑object的mspan鏈表nonemptymSpanList//沒有空閑object的mspan鏈表,或者是已被mcache取?的msapn鏈表emptymSpanList//已累計(jì)分配的對(duì)象個(gè)數(shù)nmallocuint64}empty表?這條鏈表?的mspan都被分配了object,或者是已經(jīng)被cache取?了的mspan,這個(gè)mspan就被那個(gè)?作線程獨(dú)占了。?nonempty則表?有空閑對(duì)象的mspan列表。每個(gè)central結(jié)構(gòu)體都在mheap中維護(hù)。簡(jiǎn)單說下mcache從mcentral獲取和歸還mspan的流程:獲取加鎖;從nonempty鏈表找到?個(gè)可?的mspan;并將其從nonempty鏈表刪除;將取出的mspan加?到empty鏈表;將mspan返回給?作線程;解鎖。歸還加鎖;將mspan從empty鏈表刪除;將mspan加?到nonempty鏈表;解鎖。mheapmheap:代表Go程序持有的所有堆空間,Go程序使??個(gè)mheap的全局對(duì)象_mheap來管理堆內(nèi)存。當(dāng)mcentral沒有空閑的mspan時(shí),會(huì)向mheap申請(qǐng)。?mheap沒有資源時(shí),會(huì)向操作系統(tǒng)申請(qǐng)新內(nèi)存。mheap主要?于?對(duì)象的內(nèi)存分配,以及管理未切割的mspan,?于給mcentral切割成?對(duì)象。同時(shí)我們也看到,mheap中含有所有規(guī)格的mcentral,所以,當(dāng)?個(gè)mcache從mcentral申請(qǐng)mspan時(shí),只需要在獨(dú)?的mcentral中使?鎖,并不會(huì)影響申請(qǐng)其他規(guī)格的mspan。mheap結(jié)構(gòu)體定義://path:/usr/local/go/src/runtime/mheap.gotypemheapstruct{lockmutex//spans:指向mspans區(qū)域,?于映射mspan和page的關(guān)系spans[]*mspan//指向bitmap?地址,bitmap是從?地址向低地址增長(zhǎng)的bitmapuintptr//指?arena區(qū)?地址arena_startuintptr//指?arena區(qū)已使?地址位置arena_useduintptr//指?arena區(qū)末地址arena_enduintptrcentral[67*2]struct{mcentralmcentralpad[sys.CacheLineSize-unsafe.Sizeof(mcentral{})%sys.CacheLineSize]byte}}上圖我們看到,bitmap和arena_start指向了同?個(gè)地址,這是因?yàn)閎itmap的地址是從?到低增長(zhǎng)的,所以他們指向的內(nèi)存位置相同。分配流程變量是在棧上分配還是在堆上分配,是由逃逸分析的結(jié)果決定的。通常情況下,編譯器是傾向于將變量分配到棧上的,因?yàn)樗拈_銷?,最極端的就是"zerogarbage",所有的變量都會(huì)在棧上分配,這樣就不會(huì)存在內(nèi)存碎?,垃圾回收之類的東西。Go的內(nèi)存分配器在分配對(duì)象時(shí),根據(jù)對(duì)象的??,分成三類:?對(duì)象(?于等于16B)、?般對(duì)象

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論