
Go的內存管理內存的分級arena區SpansGo中的mspan內存管理組件mcachemcentralmheap內存的分配流程內存的分級使用的是多級分配的辦法將內存分為多級進行分級管理從而降低鎖的粒度。并且將可用的堆內存采用二級管理其中每個線程會有一個自己的內存池只有在自己的內存池不夠時才會去全局內存中獲取夠用時首先使用自己的內存池。基本概念arena區arena是堆區即動態分配區域被分隔成8k大小的頁將頁組合起來稱為mspan。bitmap區域標識arena區域的那些地址保存了對象并且使用4bit標識對象是否包含指針、GC標記信息。bitmap的的一個byte大小的內存標識的是arena的4個指針大小的內存一個是8b因此bitmap的大小512GB/(4*8B)16GB。Spansspans中保存的是mspan是arena中分隔頁組成的基本內存管理單元的指針每個指針對應一頁會出現多個s指向同一個mspan每一個mspan中也保存了對應的spans在回收時可以快速的找到Go中的mspanmspan是go內存管理的基本單元是一片連續的8kb的頁組成的大塊內存。mspan的特點mspan是包含頁的起始地址頁的span規格和頁的數量的雙端鏈表關于mspan和object的關系mspan是根據自身的SizeClass來分割成若干個object一個object可儲存一個對象在為對象分配內存的時候mspan會分配一個和object大小接近的對象SizeClass和SpanClass的概念Size_Class Span_Class / 2由計算方式可以看出每一個Span_Class中有兩個mspan。一個分配給包含有指針的對象一個分配給不包含指針的對象。mspan和SizeClass在Go1.9中mspan的SizeClass有67種每個mspan分割的object大小是8*2n的倍數寫在代碼中// path: /usr/local/go/src/runtime/sizeclasses.go const _NumSizeClasses 67 var class_to_size [_NumSizeClasses]uint16{0, 8, 16, 32, 48, 64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 576, 640, 704, 768, 896, 1024, 1152, 1280, 1408, 1536,1792, 2048, 2304, 2688, 3072, 3200, 3456, 4096, 4864, 5376, 6144, 6528, 6784, 6912, 8192, 9472, 9728, 10240, 10880, 12288, 13568, 14336, 16384, 18432, 19072, 20480, 21760, 24576, 27264, 28672, 32768}在使用SizeClass的時候其中為0表示大對象直接由對內存分配小對象通過mspan來分配SizeClass也決定了mspan所能分配的頁數寫死在代碼中上圖中由兩個S指向同一個mspan這是因為兩個s指向的P同屬一個mspan因此方便了互相查找。startAddr 直接指向了arena區域表示mspan的起始地址allocBIts指向一個位圖每位代表一個塊是否被分配了對象allocCount 表示已經分配的對象個數內存管理組件Go中的分配器的3中組件的構成mcachemcentralmheapmcacheGo中為每個邏輯處理器P提供一個本地線程緩存即mcache每個P同一時間只能運行一個goroutine因此訪問mcache是不需要加鎖mcache的結構體定義//path: /usr/local/go/src/runtime/mcache.go type mcache struct { alloc [numSpanClasses]*mspan } numSpanClasses _NumSizeClasses 1mcache中使用numSpanClasses作為索引管理多個用于分配的mspan包含了所有規格的mspannumSpanClasses是mspan中介紹的_NumSizeClasses的2倍數組中的一半mspan是分配給有指針的對象一半是給沒有指針的對象。在垃圾回收的時候沒有指針的就不需要進一步掃描了mcache的初始化在初始化時沒有mspan資源使用過程中動態的從mcentral申請會緩存起來對象小于等于32k時使用mcashe中的mspan。mcentralmecntral為mcache提供切分好的mspan資源每個mcentral保存一種特定大小的全局mspan列表分配的和未分配的mcentral被所有的工作線程共同享有存在競爭有鎖機制//path: /usr/local/go/src/runtime/mcentral.go type mcentral struct { // 互斥鎖 lock mutex // 規格 sizeclass int32 // 尚有空閑object的mspan鏈表 nonempty mSpanList // 沒有空閑object的mspan鏈表或者是已被mcache取走的msapn鏈表 empty mSpanList // 已累計分配的對象個數 nmalloc uint64 }圖中的內容和結構體對應獲取mspan和歸還流程獲取加鎖從nonempty鏈表找到一個可用的mspan并將其從nonempty中刪除將取出的mspan加入到empty鏈表將mspan返回給工作線程解鎖歸還加鎖將mspan從empty鏈表刪除將mspan加入到nonempty鏈表解鎖mheapmheap代表Go程序持有的所有堆空間Go程序使用一個mheap的全局對象_mheap來管理當mcentral中沒有空閑的mspan時會向mheap申請沒有mheap時會向操作系統申請。mheap的主要工作大對象分配內存管理未切割的mspan內存的分配流程32k直接從mheap上分配16B的對象使用mcache的tiny分配器分配二者之間的首先計算對象規格然后從mcache中相應的mspan中分配如果mcache中沒有則向mcentral申請mcentral中沒有則向mheap申請mheap中沒有則找操作系統。