概述

全局內(nèi)存
Golang的內(nèi)存管理與C語言的有所區(qū)別:C語言使用Malloc進(jìn)行內(nèi)存分配,使用的是gclib提供的ptmalloc2方法;Golang的內(nèi)存分配方法類似于Google的TCMalloc,以及MC的內(nèi)存池管理方式,即:
- 預(yù)先申請一大塊全局內(nèi)存,即Arena 堆,大小為512G
- 每個線程需要使用內(nèi)存時向全局內(nèi)存申請,并維護(hù)在線程內(nèi)部的結(jié)構(gòu)中作為私有內(nèi)存
該方法相對于ptmalloc更好地支持了多線程場景,并且優(yōu)化了外碎片(類似于操作系統(tǒng)的頁式管理)
內(nèi)存塊管理
- 全局內(nèi)存Arena 內(nèi)部切分為Page,每個Page大小為8KB;
- 分配內(nèi)存時按照塊(Span)為單位,共有67種大小,最小為8Bytes,最大為32KB;大的Span需要由多頁組成,并且可能會產(chǎn)生一定的內(nèi)碎片:
- 大塊span大小為8KB,那么需要1頁,湊出1個這樣的Span,無碎片;
- 小塊span大小為480Bytes,一頁可以分配17個這樣的Span,剩余32KB為內(nèi)碎片;
目前67種SpanClass詳情如下:
- bytes/obj:塊大小
- bytes/span:實際占用的內(nèi)存大?。╬age為單位)
- objects:目前內(nèi)存占用可以得到的塊數(shù)
- tail_waste:內(nèi)碎片大小
// class bytes/obj bytes/span objects tail waste max waste
// 1 8 8192 1024 0 87.50%
// 2 16 8192 512 0 43.75%
// 3 32 8192 256 0 46.88%
// 4 48 8192 170 32 31.52%
// 5 64 8192 128 0 23.44%
// 6 80 8192 102 32 19.07%
// 7 96 8192 85 32 15.95%
// 8 112 8192 73 16 13.56%
// 9 128 8192 64 0 11.72%
// 10 144 8192 56 128 11.82%
// 11 160 8192 51 32 9.73%
// 12 176 8192 46 96 9.59%
// 13 192 8192 42 128 9.25%
// 14 208 8192 39 80 8.12%
// 15 224 8192 36 128 8.15%
// 16 240 8192 34 32 6.62%
// 17 256 8192 32 0 5.86%
// 18 288 8192 28 128 12.16%
// 19 320 8192 25 192 11.80%
// 20 352 8192 23 96 9.88%
// 21 384 8192 21 128 9.51%
// 22 416 8192 19 288 10.71%
// 23 448 8192 18 128 8.37%
// 24 480 8192 17 32 6.82%
// 25 512 8192 16 0 6.05%
// 26 576 8192 14 128 12.33%
// 27 640 8192 12 512 15.48%
// 28 704 8192 11 448 13.93%
// 29 768 8192 10 512 13.94%
// 30 896 8192 9 128 15.52%
// 31 1024 8192 8 0 12.40%
// 32 1152 8192 7 128 12.41%
// 33 1280 8192 6 512 15.55%
// 34 1408 16384 11 896 14.00%
// 35 1536 8192 5 512 14.00%
// 36 1792 16384 9 256 15.57%
// 37 2048 8192 4 0 12.45%
// 38 2304 16384 7 256 12.46%
// 39 2688 8192 3 128 15.59%
// 40 3072 24576 8 0 12.47%
// 41 3200 16384 5 384 6.22%
// 42 3456 24576 7 384 8.83%
// 43 4096 8192 2 0 15.60%
// 44 4864 24576 5 256 16.65%
// 45 5376 16384 3 256 10.92%
// 46 6144 24576 4 0 12.48%
// 47 6528 32768 5 128 6.23%
// 48 6784 40960 6 256 4.36%
// 49 6912 49152 7 768 3.37%
// 50 8192 8192 1 0 15.61%
// 51 9472 57344 6 512 14.28%
// 52 9728 49152 5 512 3.64%
// 53 10240 40960 4 0 4.99%
// 54 10880 32768 3 128 6.24%
// 55 12288 24576 2 0 11.45%
// 56 13568 40960 3 256 9.99%
// 57 14336 57344 4 0 5.35%
// 58 16384 16384 1 0 12.49%
// 59 18432 73728 4 0 11.11%
// 60 19072 57344 3 128 3.57%
// 61 20480 40960 2 0 6.87%
// 62 21760 65536 3 256 6.25%
// 63 24576 24576 1 0 11.45%
// 64 27264 81920 3 128 10.00%
// 65 28672 57344 2 0 4.91%
// 66 32768 32768 1 0 12.50%
數(shù)據(jù)結(jié)構(gòu)

內(nèi)存管理-數(shù)據(jù)結(jié)構(gòu)
如圖,Golang內(nèi)存管理分為全局內(nèi)存管理和線程內(nèi)存管理;線程內(nèi)存不足時向全局內(nèi)存申請,并且緩存在線程內(nèi)部作為私有內(nèi)存;
- 全局內(nèi)存管理:MHeap
- 【67*2】MCentral,因為MCentral是以SpanClass進(jìn)行區(qū)分的,一共67種SpanClass,這里為每種Class配備兩組,一組用于值存儲,一組用于指針存儲;
- MCentral結(jié)構(gòu)中包含Non-Empty(可分配)和Empty(未分配)的兩個雙向鏈表,每個節(jié)點為MSpan,即當(dāng)前SpanClass下所有可分配的Span集合;
注意:MCentral結(jié)構(gòu)初始化時,是空的,所有內(nèi)存都需要向MHeap中進(jìn)行申請。
- 線程內(nèi)存管理:MCache By P
- 【67*2】MSpan,線程內(nèi)的內(nèi)存管理也是按照SpanClass進(jìn)行存儲的,對應(yīng)指針/值,值的部分在GC掃描時無需掃描
- MSpan結(jié)構(gòu)中包含:含有多少個塊(小塊),使用了多少個Page(大塊),AllocBits(Bitmap,用于存儲塊的分配情況),以及當(dāng)前的SpanClass - 對應(yīng)塊的Size
注意:Mspan有前后指針,在Mcache和MCentral中均為雙向鏈表結(jié)構(gòu),用于存儲多個Mspan結(jié)構(gòu)
內(nèi)存申請流程
1.計算合適的塊大?。鹤罱咏臑閏lass=44,size=4864的塊
2.查詢當(dāng)前P對應(yīng)的MCache結(jié)構(gòu)體中對應(yīng)class=44的MSpan結(jié)構(gòu)體中是否仍有未分配完的塊:如仍有,則直接分配,在Mcache[44].AllocBits中記下分配的塊
- 如MCache[44]中的塊都已分配完,或者M(jìn)Cache[44]尚未初始化,則線程向MCentral申請內(nèi)存
- MHeap.centrals[44]中l(wèi)ock加鎖
- 查看MHeap.centrals[44]中的non-empty雙向鏈表中是否有Span,如有則直接分配,返回給進(jìn)程
- MHeap.centrals[44]中l(wèi)ock解鎖
- MCache[44]中緩存被分配的Span
- 如MHeap.centrals[44]中無空余Span或者M(jìn)heap.Centrals[44]尚未初始化,則MCentral向MHeap申請內(nèi)存
- Mheap.Lock加鎖
- MHeap從Arena中分配一塊兒內(nèi)存,arena_used向后移動
- MHeap.centrals[44]中加入對應(yīng)的Span
- Mheap.lock解鎖