解释定义:

mheap:用于管理整个堆内存,mheap 管理多个arena,arena管理多个span,一个span由多个page组成,一个arena有8192个page,page由内存块组成

mspan:一个span对应一个mspan

mcentral:mheap里有一个全局的mspan管理中心包含mcentral数组,0-135 mcentral,一个mcentral(管理对应类型的span)

 

spanclass高7为是大小编号,最后一位0代表是指针,1代表不是指针

另外分已用尽full和未用尽partial,里面又分已清扫的spanSet和未清扫的spanSet,spanSet是并发安全的。如果所有人都来mcentral要内存,那加锁太频繁了,那么自然而然每个P都有本地的小对象缓存

mcache:每个P内部的小对象缓存。alloc 0-135 *mspan,先在本地找,不够了去mcentral拿一个到本地,用完的放到mcentral的fullset中,tiny 16B,tinyoffset记录这段内存已经用到哪里了,剩下空间还够就继续分配,不够就拿16B大小的内存块过来用,本地缓存的用完了就从mcentral中拿一个新的mspan过来

heapArena:arena 对应一个heapArena 管理者

    type heapArena struct {
        bitmap [heapArenaBitmapBytes]byte 

        spans [pagesPerArena]*mspan  pagesPerArena = 8192 用于定位一个page对应的mspan在哪
        pageInUse [pagesPerArena / 8]uint8  
        pageMarks [pagesPerArena / 8]uint8   
        pageSpecials [pagesPerArena / 8]uint8
        checkmarks *checkmarksMap
        zeroedBase uintptr 标记此 arena 中尚未使用的第一个page的首地址
    }

 

bitmap最后的byte代表一字节分高4位代表终止扫描,低4位代表标量指针。



假设分配一个slice,包含指针,长度,容量。pageInUse 只标记处于使用状态的span的第一个page,例如arena的span 0-1 2-4都使用了,那么第0号元素的pageInUse 第0位标记为1 第2位标记为1

 pageMarks 同pageInUse,只标记哪些span中存在被标记的对象,在gc的时候通过这个位图来释放不含标记对象的span

spans 用于定位一个page对应的mspan在哪

 

type mspan struct {
	next *mspan     // next span in list, or nil if none
	prev *mspan     // previous span in list, or nil if none
	list *mSpanList // For debugging. TODO: Remove.

	startAddr uintptr // address of first byte of span aka s.base()
	npages    uintptr // number of pages in span

	manualFreeList gclinkptr // list of free objects in mSpanManual spans

	// freeindex is the slot index between 0 and nelems at which to begin scanning
	// for the next free object in this span.
	// Each allocation scans allocBits starting at freeindex until it encounters a 0
	// indicating a free object. freeindex is then adjusted so that subsequent scans begin
	// just past the newly discovered free object.
	//
	// If freeindex == nelem, this span has no free objects.
	//
	// allocBits is a bitmap of objects in this span.
	// If n >= freeindex and allocBits[n/8] & (1<<(n%8)) is 0
	// then object n is free;
	// otherwise, object n is allocated. Bits starting at nelem are
	// undefined and should never be referenced.
	//
	// Object n starts at address n*elemsize + (start << pageShift).
	freeindex uintptr
	// TODO: Look up nelems from sizeclass and remove this field if it
	// helps performance.
	nelems uintptr // number of object in the span.

	// Cache of the allocBits at freeindex. allocCache is shifted
	// such that the lowest bit corresponds to the bit freeindex.
	// allocCache holds the complement of allocBits, thus allowing
	// ctz (count trailing zero) to use it directly.
	// allocCache may contain bits beyond s.nelems; the caller must ignore
	// these.
	allocCache uint64

	// allocBits and gcmarkBits hold pointers to a span's mark and
	// allocation bits. The pointers are 8 byte aligned.
	// There are three arenas where this data is held.
	// free: Dirty arenas that are no longer accessed
	//       and can be reused.
	// next: Holds information to be used in the next GC cycle.
	// current: Information being used during this GC cycle.
	// previous: Information being used during the last GC cycle.
	// A new GC cycle starts with the call to finishsweep_m.
	// finishsweep_m moves the previous arena to the free arena,
	// the current arena to the previous arena, and
	// the next arena to the current arena.
	// The next arena is populated as the spans request
	// memory to hold gcmarkBits for the next GC cycle as well
	// as allocBits for newly allocated spans.
	//
	// The pointer arithmetic is done "by hand" instead of using
	// arrays to avoid bounds checks along critical performance
	// paths.
	// The sweep will free the old allocBits and set allocBits to the
	// gcmarkBits. The gcmarkBits are replaced with a fresh zeroed
	// out memory.
	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                uint32        // for divide by elemsize
	allocCount            uint16        // number of allocated objects
	spanclass             spanClass     // size class and noscan (uint8)
	state                 mSpanStateBox // mSpanInUse etc; accessed atomically (get/set methods)
	needzero              uint8         // needs to be zeroed before allocation
	allocCountBeforeCache uint16        // a copy of allocCount that is stored just before this span is cached
	elemsize              uintptr       // computed from sizeclass or from npages
	limit                 uintptr       // end of data in span
	speciallock           mutex         // guards specials list
	specials              *special      // linked list of special records sorted by offset.

	// freeIndexForScan is like freeindex, except that freeindex is
	// used by the allocator whereas freeIndexForScan is used by the
	// GC scanner. They are two fields so that the GC sees the object
	// is allocated only when the object and the heap bits are
	// initialized (see also the assignment of freeIndexForScan in
	// mallocgc, and issue 54596).
	freeIndexForScan uintptr
}

 

spanclass 跟mcentral一样记录着划分好的内存块规格

nelem 记录当前span共划分成了多少个内存块

freeIndex 记录着下个空闲内存块的索引

allocBits *gcBits 用于标记哪些内存块已经被分配了

gcmarkBits *gcBits 当前span的标记位图,一个二进制位对应span的一个内存块,gc清扫的时候释放allocBits,然后把gcmarkBits用作allocBits,未被gc标记的内存块就能回收利用了

 

申请内存的步骤:

1.辅助gc
至少扫描64KB,多干的部分会存在银行。全局gccontroller有足够多的信用,窃取了也不用进行辅助gc
2.空间分配
<16B noscan tiny
>=16B <=32KB noscan scannable
>32KB 大块内存分配器 需要多少字节就分配一个对应大小的新的span
3.位图标记

*heapArena放到一个数组里,并用arena编号作为索引定位heapArena windows/linux架构arena大小不同,所以有二维数组的说法,计算出arenaidx
amd64 windows第一维64,占6bit,第二维数组长度为1M=2^20,arenaidx中低20位用作第二维的索引
amd64 linux第一维一个元素,第二维4M=2^22,arenaidx中低22位用作第二维的索引
每个arena中有pagesPerArena个page,每个page大小是pageSize,一个内存块的page编号=(p/pageSize)%pagesPerArena,最终能在heapArena数组中找到mspan地址了
4.收尾工作

当前处于gc标记阶段,需要对新分配的对象进行gc标记,这次内存分配达到了触发gc的条件,还会触发新一轮的gc

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。