1.mspan源码
type mspan struct {
//mspan是一个双向链表结构,所以包含next指针和prev指针,分别指向后继节点和前驱节点
next *mspan
prev *mspan
// 好像是用来debug的,具体不是很清楚
list *mSpanList
// mspan主要是用来管理heap当中的内存,其实质为heap当中一连串页组成的一个结构,所以,startAddr保存的是该mspan从哪个页开始管理
startAddr uintptr
// mspan中保存的heap当中页的数量
npages uintptr
//暂时还不太理解
manualFreeList gclinkptr
// 空闲对象的下标
freeindex uintptr
//msapn当中包含的object数量,可以根据npages和elementSize推算出来
nelems uintptr
//位图,方便查看对象是否分配
allocCache uint64
//用来标记mspan当中elem的使用情况
allocBits *gcBits
//用来标记maspan当中对象是否被标记,因为go用的是标记清除算法
gcmarkBits *gcBits
//当前GC阶段,具体数值可看源码
sweepgen uint32
// for divide by elemsize - divMagic.mul
divMul uint16
// if non-0, elemsize is a power of 2, & this will get object allocation base
baseMask uint16
//已经分配的object数量
allocCount uint16
//当前mspan对象大小,划分依据
spanclass spanClass
//当前msapn的状态,空闲、垃圾回收、正在使用
state mSpanState
//在分配前是否需要清零
needzero uint8
// for divide by elemsize - divMagic.shift
divShift uint8
// for divide by elemsize - divMagic.shift2
divShift2 uint8
//这个msapn中是否需要释放内存
scavenged bool
//object对象大小
elemsize uintptr
//这个msapn中的最后一个object
limit uintptr
// guards specials list
speciallock mutex
// linked list of special records sorted by offset.
specials *special
}
综上所述,mspan是go当中内存管理的基本单元,是由一片连续的 8KB的页组成的大块内存,1.它包含有startAddr以及npages可以确定其管理范围;
2.包含spanclass和elemsize可以确定当中每一个object的大小;
3.包含freeindex可以确定空闲对象的下表;
4.包含allocBits和gcmarkBits可以用来确定mspan中的分配情况以及垃圾回收过程中的标记情况。
5.包含的scavenged和sweegen可以表示垃圾回收的阶段如:是否需要回收、正在回收、正准备使用等。
2.mcahe源码
Go 像 TCMalloc 一样为每一个逻辑处理器P 提供一个本地span缓存称作 mcache。如果 协程 需要内存可以直接从 mcache 中获取,由于在同一时间只有一个 协程运行在 逻辑处理器P上,所以中间不需要任何锁的参与。mcache 包含所有大小规格的 mspan 作为缓存,但是每种规格大小只包含一个。
type mcache struct {
// The following members are accessed on every malloc,
// so they are grouped here for better caching.
// trigger heap sample after allocating this many bytes
next_sample uintptr
//堆分配的,可扫描的字节数
local_scan uintptr
//tiny对象在堆中的起始地址
tiny uintptr
//tiny对象在堆中的偏移地址
tinyoffset uintptr
//分配tiny对象的数量
local_tinyallocs uintptr // number of tiny allocs not counted in other stats
// The rest is not accessed on every malloc.
//每一个mcache中都包含了67*2种msapn,67种是msapn规定的大小,而在mcache中分成两组,一组包含指针,一组不包含指针,便于gc
alloc [numSpanClasses]*mspan
//没太看懂
stackcache [_NumStackOrders]stackfreelist
//large object释放的内存大小
local_largefree uintptr
//large object被释放的数量
local_nlargefree uintptr
//每种mspan被释放的数量
local_nsmallfree [_NumSizeClasses]uintptr
//是否需要gc
flushGen uint32
}
综上所述
1.mcache中包含了tiny和tinyoffset对象用来分配微对象(<16B)
2.mcache中包含了alloc对象,该对象是67种msapn的集合,可以用来分配小对象(<32KB)。
3.mcache无法分配大对象,由堆直接分配(>32KB)
3.mcentral源码
mcentral 对象收集所有给定规格大小的 span。每一个 mcentral 都包含两个 mspan 的链表:
做这种区主要是为了更快的分配span到mcache中。
除了级别0,每一个级别都会有一个mcentral,管理span列表。
nonempty mspanList – 有空闲对象的 span链表
empty mspanList – 没有空闲对象或 span 已经被 mcache 缓存的 span 链表
type mcentral struct {
//因为mcentral是全局的变量,每个线程不够了都要向mcentral申请,所以要有锁
lock mutex
//2*sizeclass,包含scan和nonscan两种
spanclass spanClass
//type mSpanList struct {
//first *mspan // first span in list, or nil if none
//last *mspan // last span in list, or nil if none
//}
//mspanList结构如上所示,就是一个msapn的列表,包含头尾
//nonempty是已分配对象的列表
nonempty mSpanList
//未分配mspan对象列表
empty mSpanList
//已分配对象的个数
nmalloc uint64
}
思路:这里的申请感觉就像是从库存里拿
4.mheap源码
而所有级别的这些mcentral,其实是一个数组,由 mheap进行管理。
mheap的作用不只是管理central,另外大对象也会直接通过mheap进行分配。mheap实现了对于虚拟内存线性地址空间的精准管理,建立了span与具体线性地址空间的联系,保存了分配的位图信息,是管理内存的最核心单元。后面我们还会看到,堆区的内存还被分成了HeapArea大小进行管理。对heap进行的操作必须得全局加锁,而不管是mcache、mcentral都只能看做是某种形式的缓存。
type mheap struct {
// lock must only be acquired on the system stack, otherwise a g
// could self-deadlock if its stack grows with the lock held.
lock mutex
//mtreap 好像是go当中的一种组织形式
free mTreap
//垃圾回收的状态信息,与mspan当中的sweegen类似
sweepgen uint32
// all spans are swept
sweepdone uint32
// number of active sweepone calls
sweepers uint32
//所有mspan的存储
allspans []*mspan
//两个栈,一个是回收后正在使用的,一个是还没回收正在使用的?还是一个是正在使用的,一个是还没划分的?
sweepSpans [2]gcSweepBuf
// align uint64 fields on 32-bit for atomics
_ uint32
//正在使用的pages
pagesInUse uint64
//在本次gc种回收的page
pagesSwept uint64
//pagesSwept to use as the origin of the sweep ratio; updated atomically
pagesSweptBasis uint64
// value of heap_live to use as the origin of sweep ratio; written with lock, read without
sweepHeapLiveBasis uint64
//proportional sweep ratio; written with lock, read without
sweepPagesPerByte float64
//下面的没看懂
scavengeTimeBasis int64
scavengeRetainedBasis uint64
scavengeBytesPerNS float64
scavengeRetainedGoal uint64
scavengeGen uint64 // incremented on each pacing update
reclaimIndex uint64
reclaimCredit uintptr
// Malloc stats.
//大对象已经分配的比特数
largealloc uint64
//大对象已经分配的数量
nlargealloc uint64
//大对象回收的比特数
largefree uint64
//大对象回收的数量
nlargefree uint64
//小对象回收的数量
nsmallfree [_NumSizeClasses]uint64
// arenas is the heap arena map. It points to the metadata for
// the heap for every arena frame of the entire usable virtual
// address space.
arenas [1 << arenaL1Bits]*[1 << arenaL2Bits]*heapArena
//预分配空间
heapArenaAlloc linearAlloc
// arenaHints is a list of addresses at which to attempt to
// add more heap arenas. This is initially populated with a
// set of general hint addresses, and grown with the bounds of
// actual heap arena ranges.
arenaHints