详解Go中内存分配 (4)

在分配内存的时候是按页来进行分配的,每个页的大小是_PageSize(8K),然后需要根据传入的size来判断需要分多少页,最后调用alloc从堆上分配。

func (h *mheap) alloc(npages uintptr, spanclass spanClass, needzero bool) *mspan { var s *mspan systemstack(func() { if h.sweepdone == 0 { // 回收一部分内存 h.reclaim(npages) } // 进行内存分配 s = h.allocSpan(npages, false, spanclass, &memstats.heap_inuse) }) ... return s }

继续看allocSpan的实现:

const pageCachePages = 8 * unsafe.Sizeof(pageCache{}.cache) func (h *mheap) allocSpan(npages uintptr, manual bool, spanclass spanClass, sysStat *uint64) (s *mspan) { // Function-global state. gp := getg() base, scav := uintptr(0), uintptr(0) pp := gp.m.p.ptr() // 申请的内存比较小,尝试从pcache申请内存 if pp != nil && npages < pageCachePages/4 { c := &pp.pcache if c.empty() { lock(&h.lock) *c = h.pages.allocToCache() unlock(&h.lock) } base, scav = c.alloc(npages) if base != 0 { s = h.tryAllocMSpan() if s != nil && gcBlackenEnabled == 0 && (manual || spanclass.sizeclass() != 0) { goto HaveSpan } } } lock(&h.lock) // 内存比较大或者线程的页缓存中内存不足,从mheap的pages上获取内存 if base == 0 { base, scav = h.pages.alloc(npages) // 内存也不够,那么进行扩容 if base == 0 { if !h.grow(npages) { unlock(&h.lock) return nil } // 重新申请内存 base, scav = h.pages.alloc(npages) // 内存不足,抛出异常 if base == 0 { throw("grew heap, but no adequate free space found") } } } if s == nil { // 分配一个mspan对象 s = h.allocMSpanLocked() } unlock(&h.lock) HaveSpan: // 设置参数初始化 s.init(base, npages) ... // 建立mheap与mspan之间的联系 h.setSpans(s.base(), npages, s) ... return s }

这里会根据需要分配的内存大小再判断一次:

如果要分配的页数小于pageCachePages/4=64/4=16页,那么就尝试从pcache申请内存;

如果申请的内存比较大或者线程的页缓存中内存不足,会通过runtime.pageAlloc.alloc从页堆分配内存;

如果页堆上内存不足,那么就mheap的grow方法从系统上申请内存,然后再调用pageAlloc的alloc分配内存;

下面来看看grow的向操作系统申请内存:

func (h *mheap) grow(npage uintptr) bool { // We must grow the heap in whole palloc chunks. ask := alignUp(npage, pallocChunkPages) * pageSize totalGrowth := uintptr(0) nBase := alignUp(h.curArena.base+ask, physPageSize) // 内存不够则调用sysAlloc申请内存 if nBase > h.curArena.end { av, asize := h.sysAlloc(ask) if av == nil { print("runtime: out of memory: cannot allocate ", ask, "-byte block (", memstats.heap_sys, " in use)\n") return false } // 重新设置curArena的值 if uintptr(av) == h.curArena.end { h.curArena.end = uintptr(av) + asize } else { if size := h.curArena.end - h.curArena.base; size != 0 { h.pages.grow(h.curArena.base, size) totalGrowth += size } h.curArena.base = uintptr(av) h.curArena.end = uintptr(av) + asize } nBase = alignUp(h.curArena.base+ask, physPageSize) } ... return true }

grow会通过curArena的end值来判断是不是需要从系统申请内存;如果end小于nBase那么会调用runtime.mheap.sysAlloc方法从操作系统中申请更多的内存;

func (h *mheap) sysAlloc(n uintptr) (v unsafe.Pointer, size uintptr) { n = alignUp(n, heapArenaBytes) // 在预先保留的内存中申请一块可以使用的空间 v = h.arena.alloc(n, heapArenaBytes, &memstats.heap_sys) if v != nil { size = n goto mapped } // 根据页堆的arenaHints在目标地址上尝试扩容 for h.arenaHints != nil { hint := h.arenaHints p := hint.addr if hint.down { p -= n } if p+n < p { // We can't use this, so don't ask. v = nil } else if arenaIndex(p+n-1) >= 1<<arenaBits { // Outside addressable heap. Can't use. v = nil } else { // 从操作系统中申请内存 v = sysReserve(unsafe.Pointer(p), n) } if p == uintptr(v) { // Success. Update the hint. if !hint.down { p += n } hint.addr = p size = n break } if v != nil { sysFree(v, n, nil) } h.arenaHints = hint.next h.arenaHintAlloc.free(unsafe.Pointer(hint)) } ... // 将内存由Reserved转为Prepared sysMap(v, size, &memstats.heap_sys) mapped: // Create arena metadata. // 初始化一个heapArena来管理刚刚申请的内存 for ri := arenaIndex(uintptr(v)); ri <= arenaIndex(uintptr(v)+size-1); ri++ { l2 := h.arenas[ri.l1()] if l2 == nil { l2 = (*[1 << arenaL2Bits]*heapArena)(persistentalloc(unsafe.Sizeof(*l2), sys.PtrSize, nil)) if l2 == nil { throw("out of memory allocating heap arena map") } atomic.StorepNoWB(unsafe.Pointer(&h.arenas[ri.l1()]), unsafe.Pointer(l2)) } var r *heapArena r = (*heapArena)(h.heapArenaAlloc.alloc(unsafe.Sizeof(*r), sys.PtrSize, &memstats.gc_sys)) ... // 将创建heapArena放入到arenas列表中 h.allArenas = h.allArenas[:len(h.allArenas)+1] h.allArenas[len(h.allArenas)-1] = ri atomic.StorepNoWB(unsafe.Pointer(&l2[ri.l2()]), unsafe.Pointer(r)) } return }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppyzy.html