golang源码解析--内存mspan,mcache结构体

mspan简介

Go中内存管理的基本单元,是由一片连续的 8KB的页组成的大块内存。注意,这里的页和操作系统本身的页并不是一回事,它一般是操作系统页大小的几倍。一句话概括: mspan是一个包含起始地址、 mspan规格、页的数量等内容的双端链表。
关于span的状态:

关于mspan的状态转换:

当mspan在堆的空闲treap(详情见备注)上,它的状态为mSpanFree
当mspan在清除treap上,当且仅当此时,scavengedtrue,其他所有情况该变量为false
当mspan是被使用的,它的状态为mSpanInUse,或者mSpanManual
1.span将从free到in-use或者manual在GC的任何阶段
2.在清除阶段gcphase
_GCoff,span可以从in-use或者manual到free,in-use的转变对应的是清除,manual的转变对应的是堆栈被释放。
3.在整个gc,gcphase!=_GCoff,span不可以从manual或者in-use到free。因为当前GC读取指针并遍历它的span,span的状态必须保持单调。

mspan的图

mspan中allbits记录着哪些元素是已分配的,哪些未分配。alloccache用数字按位代表freeindex开始的
golang源码解析--内存mspan,mcache结构体_第1张图片

源码分析

1.mspanlist

//表示span的列表
//go:notinheap
type mSpanList struct {
	first *mspan // first span in list, or nil if none
	last  *mspan // last span in list, or nil if none
}

2.mspan

type mspan struct {
	//指向下一个span的指针
	next *mspan     
	//指向上一个span的指针
	prev *mspan     
	list *mSpanList 
    //span第一个字节的地址
	startAddr uintptr 
    //当前mspan的页数
	npages    uintptr // number of pages in span
	//在mSpanManual的空闲对象
	manualFreeList gclinkptr 
	//freeindex标记0~belems之间的插槽索引,标记的的是在span中的下一个空闲对象
	//每次分配内存都从freeindex开始,直到遇到表示空闲对象的地方,之后调整freeindex使得下一次扫描能跳过上一次的分配
	//若freeindex==nelem,则当前span没有了空余对象
	//
	//allocBits是这个span的位图
	freeindex uintptr
	// TODO: Look up nelems from sizeclass and remove this field if it
	// helps performance.
	nelems uintptr // number of object in the span.

	//在freeindex处的allocBits的缓存.
	//allocCache的最低位对应于freeindex位。
	//allocCache保留allocBits的补码,从而允许ctz(计数尾随零)直接使用它。
	//allocCache可能包含s.nelems以外的位; 呼叫者必须忽略这些。
	//作用是记录未被使用的地址
	allocCache uint64

	//allocBits标记span中的elem哪些是被使用的,哪些是未被使用的
	//gc,arkBits标记span中的elem哪些是被标记了的,哪些是未被标记的
	allocBits  *gcBits
	gcmarkBits *gcBits

	// 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
	// if sweepgen == h->sweepgen + 1, the span was cached before sweep began and is still cached, and needs sweeping
	// if sweepgen == h->sweepgen + 3, the span was swept and then cached and is still cached
	// h->sweepgen is incremented by 2 after every GC

	sweepgen    uint32
	divMul      uint16     // for divide by elemsize - divMagic.mul
	baseMask    uint16     // if non-0, elemsize is a power of 2, & this will get object allocation base
	allocCount  uint16     // number of allocated objects
	spanclass   spanClass  // size class and noscan (uint8)
	state       mSpanState // mspaninuse etc
	needzero    uint8      // needs to be zeroed before allocation
	divShift    uint8      // for divide by elemsize - divMagic.shift
	divShift2   uint8      // for divide by elemsize - divMagic.shift2
	scavenged   bool       // whether this span has had its pages released to the OS
	elemsize    uintptr    // computed from sizeclass or from npages
	unusedsince int64      // first time spotted by gc in mspanfree state
	limit       uintptr    // end of data in span
	speciallock mutex      // guards specials list
	specials    *special   // linked list of special records sorted by offset.
}

备注:
treap:是指有一个随机附加域满足堆的性质的二叉搜索树,其结构相当于以随机数据插入的二叉搜索树。

mcache简介:

Gorontine 的运行都是绑定到一个 P 上面,mcache 是每个 P 的 cache。
好处是不需要加锁就可以完成内存的分配。

源码分析

type mcache struct {
	// The following members are accessed on every mallc,
	// so they are grouped here for better caching.
	next_sample int32   // trigger heap sample after allocating this many bytes
	local_scan  uintptr // bytes of scannable heap allocated

	// Allocator cache for tiny objects w/o pointers.
	// See "Tiny allocator" comment in malloc.go.

	// tiny points to the beginning of the current tiny block, or
	// nil if there is no current tiny block.
	//
	// tiny is a heap pointer. Since mcache is in non-GC'd memory,
	// we handle it by clearing it in releaseAll during mark
	// termination.
	tiny             uintptr
	tinyoffset       uintptr
	local_tinyallocs uintptr // number of tiny allocs not counted in other stats

	// The rest is not accessed on every malloc.
	//alloc是span数组,长度是67 << 1,说明每种size class有2组元素。第一组span对象中包含了指针,叫做scan,表示需要gc scan;第二组没有指针,叫做noscan。提高gc scan性能。
	alloc [numSpanClasses]*mspan // spans to allocate from, indexed by spanClass

	stackcache [_NumStackOrders]stackfreelist

	// Local allocator stats, flushed during GC.
	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)

	// flushGen indicates the sweepgen during which this mcache
	// was last flushed. If flushGen != mheap_.sweepgen, the spans
	// in this mcache are stale and need to the flushed so they
	// can be swept. This is done in acquirep.
	flushGen uint32
}

参考:
https://www.jianshu.com/p/2a899f940f68

你可能感兴趣的:(golang源码分析)