Go語言——記憶體管理
Go語言——記憶體管理
參考:
ofollow,noindex">圖解 TCMalloc
問題
- 記憶體碎片:避免記憶體碎片,提高記憶體利用率。
- 多執行緒:穩定性,效率問題。
記憶體分配

記憶體劃分
(512GB/8KB) * 指標大小8byte = 512M 512GB / (指標大小(8 byte) * 8 / 2) = 16G
分配細節
- object size > 32K,則使用 mheap 直接分配。
- object size < 16 byte,不包含指標使用 mcache 的小物件分配器 tiny 直接分配;包含指標分配策略與[16 B, 32 K]類似。
- object size >= 16 byte && size <=32K byte 時,先使用 mcache 中對應的 size class 分配。
- 如果 mcache 對應的 size class 的 span 已經沒有可用的塊,則向 mcentral 請求。
- 如果 mcentral 也沒有可用的塊,則向 mheap 申請,並切分。
- 如果 mheap 也沒有合適的 span,則向作業系統申請。
span
可以看出span是一個非常重要的資料結構,每個span包含若干個連續的page。
小物件分配會在span page中劃分更小的粒度;大物件通過多頁實現。
size class
go1.10\src\runtime\sizeclasses.go
// classbytes/objbytes/spanobjectstail wastemax waste //1881921024087.50% //2168192512043.75% //3328192256046.88% //44881921703231.52% //5648192128023.44% //68081921023219.07% //7968192853215.95% //81128192731613.56% //9128819264011.72% //1014481925612811.82% //... //652867257344204.91% //6632768327681012.50%
上表中每列含義如下:
- class: class ID,每個span結構中都有一個class ID, 表示該span可處理的物件型別
- bytes/obj:該class代表物件的位元組數
- bytes/span:每個span佔用堆的位元組數,也即頁數*頁大小
- objects: 每個span可分配的物件個數,也即(bytes/spans)/(bytes/obj)
- tail bytes: 每個span產生的記憶體碎片,也即(bytes/spans)%(bytes/obj)
上表可見最大的物件是32K大小,超過32K大小的由特殊的class表示,該class ID為0,每個class只包含一個物件。所以上面只有列出了1-66。
有點像裝箱演算法,按照規格分配,減少記憶體碎片。
struct
span是記憶體管理的基本單位,每個span用來管子特定的size class物件,根據size class,span將若干個頁分成塊進行管理。
go1.10\src\runtime\mheap.go
type mspan struct { next *mspan// next span in list, or nil if none prev *mspan// previous span in list, or nil if none startAddr uintptr // address of first byte of span aka s.base() npagesuintptr // number of pages in span nelems uintptr // number of object in the span. allocBits*gcBits gcmarkBits *gcBits allocCountuint16// number of allocated objects spanclassspanClass// size class and noscan (uint8) elemsizeuintptr// computed from sizeclass or from npages }

10
以size class 10為例,npages=1,nelems=56,spanclass=10,elemsize=144;startAddr指arena區位置;next和prev指spans區,span連結串列;allocBits是一個bitmap,標記分配塊分配情況,這個設計我也用過,之前用redis bitmap實現了IPAM。
cache
從上面我們知道go通過span來分配記憶體,那在哪裡用span?通過之前的學習 Go語言——goroutine併發模型 ,我們知道每個P都有mcache,通過mcache管理每個G需要的記憶體。
go1.10\src\runtime\mcache.go
type mcache struct { tinyuintptr tinyoffsetuintptr alloc [numSpanClasses]*mspan // spans to allocate from, indexed by spanClass } numSpanClasses = _NumSizeClasses << 1 _NumSizeClasses = 67
alloc是span陣列,長度是67 << 1,說明每種size class有2組元素。第一組span物件中包含了指標,叫做scan,表示需要gc scan;第二組沒有指標,叫做noscan。提高gc scan效能。
mcache初始沒有span,G先從central動態申請span,並快取在cache。
central
go1.10\src\runtime\mcentral.go
type mcentral struct { lockmutex spanclass spanClass nonemptymSpanList // list of spans with a free object, ie a nonempty free list emptymSpanList // list of spans with no free objects (or cached in an mcache) // nmalloc is the cumulative count of objects allocated from // this mcentral, assuming all spans in mcaches are // fully-allocated. Written atomically, read under STW. nmalloc uint64 }
- lock: 多個G併發從central申請span,所以需要lock,保證一致性
- spanclass : 每個mcentral管理著一組有相同size class的span列表
- nonempty: 指還有記憶體可用的span列表
- empty: 指沒有記憶體可用的span列表
- nmalloc: 指累計分配的物件個數
執行緒從central獲取span步驟如下:
- 加鎖
- 從nonempty列表獲取一個可用span,並將其從連結串列中刪除
- 將取出的span放入empty連結串列
- 將span返回給執行緒
- 解鎖
- 執行緒將該span快取進cache
執行緒將span歸還步驟如下:
- 加鎖
- 將span從empty列表刪除
- 將span加入nonempty列表
- 解鎖
heap
central只管理特定的size class span,所以必然有一個更上層的資料結構,管理所有的sizeclass central,這就是heap。
go1.10\src\runtime\mheap.go
type mheap struct { lockmutex spans []*mspan // Malloc stats. largeallocuint64// bytes allocated for large objects nlargealloc uint64// number of large object allocations largefreeuint64// bytes freed for large objects (>maxsmallsize) nlargefreeuint64// number of frees for large objects (>maxsmallsize) // range of addresses we might see in the heap bitmapuintptr // Points to one byte past the end of the bitmap bitmap_mapped uintptr arena_start uintptr arena_useduintptr // Set with setArenaUsed. arena_alloc uintptr arena_enduintptr arena_reserved bool central [numSpanClasses]struct { mcentral mcentral pad[sys.CacheLineSize - unsafe.Sizeof(mcentral{})%sys.CacheLineSize]byte } }
- spans:對映span -> page
- large:大物件,>32K
- bitmap: gc
- arena: arena區相關資訊,pages,堆區
- central:通過size class管理span,每種size class對應兩個central

heap