核心数据结构
Go内存管理模块的核心数据结构比较少:
- mheap:管理全局的从os申请的虚拟内存空间;
- mspan:将mheap按照固定大小切分而成的细粒度的内存区块,每个区块映射了虚拟内存中的若干连续页面,页大小由Go内部定义;
- mcache:与线程相关缓存,该结构的存在是为了减少内存分配时的锁操作,优化内存分配性能。
- mcentral:集中内存池,线程在本地分配失败后,尝试向mcentral申请,如果mcentral也没有资源,则尝试向mheap分配。
mheap
type mheap struct {
lock mutex
// 空闲mspan链表
free [_MaxMHeapList]mspan
freelarge mspan
busy [_MaxMHeapList]mspan
busylarge mspan
allspans **mspan
gcspans **mspan
nspan uint32
sweepgen uint32
sweepdone uint32
spans **mspan
spans_mapped uintptr
// Proportional sweep
spanBytesAlloc uint64 // bytes of spans allocated this cycle; updated atomically
pagesSwept uint64 // pages swept this cycle; updated atomically
sweepPagesPerByte float64 // proportional sweep ratio; written with lock, read without
// Malloc stats.
largefree uint64
nlargefree uint64
nsmallfree [_NumSizeClasses]uint64
// range of addresses we might see in the heap
bitmap uintptr
bitmap_mapped uintptr
arena_start uintptr
arena_used uintptr // always mHeap_Map{Bits,Spans} before updating
arena_end uintptr
arena_reserved bool
// central free lists for small size classes.
// the padding makes sure that the MCentrals are
// spaced CacheLineSize bytes apart, so that each MCentral.lock
// gets its own cache line.
central [_NumSizeClasses]struct {
mcentral mcentral
pad [_CacheLineSize]byte
}
spanalloc fixalloc // allocator for span*
cachealloc fixalloc // allocator for mcache*
specialfinalizeralloc fixalloc // allocator for specialfinalizer*
specialprofilealloc fixalloc // allocator for specialprofile*
speciallock mutex // lock for special record allocators.
}
mheap内部主要维护多级free以及busy mspan链表。每个级别的mspan链表上的mspan总大小相同(即映射的内存页面数一样)。
除此之外,mheap内部还记录了必须的管理信息,如记录位图位置等。这些我们在后面遇到的时候作一一分析。
mspan
type mspan struct {
// 每个mspan会根据状态被link到特定的双向链表中(如free、busy链表)
next *mspan
prev *mspan
// 该mspan映射的起始内存page以及page数
start pageID
npages uintptr
freelist gclinkptr
// sweep generation:
// if sweepgen == h->sweepgen - 2, the span needs sweeping
// if sweepgen == h->sweepgen - 1, the span is currently being swept
// if sweepgen == h->sweepgen, the span is swept and ready to use
// h->sweepgen is incremented by 2 after every GC
sweepgen uint32
divMul uint32
ref uint16
sizeclass uint8 // size class
incache bool // being used by an mcache
state uint8 // mspaninuse etc
needzero uint8 // needs to be zeroed before allocation
divShift uint8
divShift2 uint8
// ???
elemsize uintptr
unusedsince int64 // first time spotted by gc in mspanfree state
npreleased uintptr // number of pages released to the os
limit uintptr // end of data in span
speciallock mutex // guards specials list
specials *special // linked list of special records sorted by offset.
baseMask uintptr // if non-0, elemsize is a power of 2, & this will get object allocation base
}
mspan中记录的最关键信息是freelist:所有的对象分配最终都被委派到从mspan上以分配合适大小的内存空间。因此,mspan在经历分配、释放等过程之后,也会变得支离破碎,如下图所示:
mcache
// Per-thread (in Go, per-P) cache for small objects.
// No locking needed because it is per-thread (per-P).
type mcache struct {
// The following members are accessed on every malloc,
// so they are grouped here for better caching.
next_sample int32
local_cachealloc uintptr
local_scan uintptr
// Allocator cache for tiny objects w/o pointers.
// See "Tiny allocator" comment in malloc.go.
tiny unsafe.Pointer
tinyoffset uintptr
local_tinyallocs uintptr // number of tiny allocs not counted in other stats
// The rest is not accessed on every malloc.
alloc [_NumSizeClasses]*mspan
// Local allocator stats, flushed during GC.
local_nlookup uintptr // number of pointer lookups
local_largefree uintptr // bytes freed for large objects (>maxsmallsize)
local_nlargefree uintptr // number of frees for large objects (>maxsmallsize)
local_nsmallfree [_NumSizeClasses]uintptr // number of frees for small objects (<=maxsmallsize)
}
mcentral
// Central list of free objects of a given size.
type mcentral struct {
lock mutex
// mcentral负责分配的内存块大小
sizeclass int32
// 拥有空闲object的mspan链表
nonempty mspan
// 无空闲object的mspan链表
empty mspan
}
数据结构关系图
- mheap管理向os申请、释放、组织span;
- mcentral按照自己管理的块大小将span划分成多个小block,并分配给mcache;
- mspan是数据的实际存储区域,按照central管理的块规格被切分成小block;
- mcache管理不同规格(块大小)的mspan:规格相同的mspan被链接到同一个链表中。