首先列一个层级架构:
1. 最底层mheap(和操作系统内存打交道)
mheap中包含着mache组件和mcentral组件
mheap向操作系统申请内存空间,然后分配给mcentral
2. 第二级mcentral组件
3. 第一级mcache组件(小对象最优先看这个可不可以申请内存)
Go会在程序运行时调用一个内存管理初始化的函数mallocinit
如下:
在这个方法中会初始化堆组件(堆组件结构体名称叫mheap)
func mallocinit() {
//...
// Initialize the heap.
//初始化堆 注意:对象变量名叫mheap_,结构体类型定义是mheap
mheap_.init()
//...
//初始化核心组件mcache
_g_.m.mcache = allocmcache()
//...
} 接下来一切都要围绕着这个mheap堆组件来展开了
我们来看看这个mheap结构体里面的关键组成部分:
type mheap struct {
//一.mheap管理的重要内存组件!!!!!
//mheap堆自己管理的容器组件,大对象申请直接放到这里,mcache和mcentral中的对象过多也会迁移到这里一部分
allspans []*mspan // all spans out there
//二.重点!!!!核心内存组件二 central
central [numSpanClasses]struct {
mcentral mcentral
pad [cpu.CacheLinePadSize - unsafe.Sizeof(mcentral{})%cpu.CacheLinePadSize]byte
}
//span分配器,用于申请内存创建span容器
spanalloc fixalloc // allocator for span*
//三.内存容器组件三是mcache,只不过在别处通过cachealloc分配器进行创建的
//mcache分配器,用于创建mcache,只不过在别处创建的组件
mcachealloc fixalloc // allocator for mcache*
} 在下面这里才初始化了mcache
// Initialize the heap.
mheap_.init()
_g_ := getg()
//这一步骤真正的初始化了Cache内存组件
_g_.m.mcache = allocmcache() 我们看看它内部是如何实现的:
// dummy mspan that contains no free objects.
//空的mspan容器,回忆一下,真正的对象是存储在哪里?
//mspan--->page---->obj
var emptymspan mspan
func allocmcache() *mcache {
var c *mcache
systemstack(func() {
lock(&mheap_.lock)
//1.核心步骤(1)调用alloc方法创建*mcache指针对象
c = (*mcache)(mheap_.cachealloc.alloc())
c.flushGen = mheap_.sweepgen
unlock(&mheap_.lock)
})
//2.核心步骤(2)
for i := range c.alloc {
//为c(这个c就是mcache组件)分配控的mspan,这样之后数据装入mspan中就可以了
c.alloc[i] = &emptymspan
}
//这里瞄了一眼,类似于递归
c.next_sample = nextSample()
return c
} 核心组件mcache(每个线程都有一个)
type mcache struct {
// The rest is not accessed on every malloc.
//重点看这里!!!!!
//mcahe这个组件中的数据都存在这里!!!! 存在这个长度为67的mspan数组中
//存储数据的时候先根据对象申请的内存大小匹配到67个mspan中的合适大小的那个mspan,然后mspan是个链表,
//在mspan链表后添加一个mspan,把数据存进去即可,记住mcache这个结构体中的都是存储的小对象
//numSpanClasses=67,每个代表一个size大小类型,参考前面的size表格
alloc [numSpanClasses]*mspan // spans to allocate from, indexed by spanClass
} go语言中cache的概念和Java JVM中的栈内存类似,每启动一个线程对应一个cache,Central和Heap是所有线程共享的。
// Initialize the heap.
//初始化mheap
func (h *mheap) init() {
//spanalloc是一个span内存管理分配器,调用spanalloc的init方法初始化hmeap中的allspans([]mspan类型)数组
//allspans([]mspan类型)数组实质上就是hmeap的内存容器,大对象都是存储在这里的,cache和central中的小对象太多了的时候
//也会把一部分迁移到allspans([]mspan类型)这个内存容器当中
h.spanalloc.init(unsafe.Sizeof(mspan{}), recordspan, unsafe.Pointer(h), &memstats.mspan_sys)
h.cachealloc.init(unsafe.Sizeof(mcache{}), nil, nil, &memstats.mcache_sys)
h.specialfinalizeralloc.init(unsafe.Sizeof(specialfinalizer{}), nil, nil, &memstats.other_sys)
h.specialprofilealloc.init(unsafe.Sizeof(specialprofile{}), nil, nil, &memstats.other_sys)
h.arenaHintAlloc.init(unsafe.Sizeof(arenaHint{}), nil, nil, &memstats.other_sys)
// Don't zero mspan allocations. Background sweeping can
// inspect a span concurrently with allocating it, so it's
// important that the span's sweepgen survive across freeing
// and re-allocating a span to prevent background sweeping
// from improperly cas'ing it from 0.
//
// This is safe because mspan contains no heap pointers.
h.spanalloc.zero = false
// h->mapcache needs no init
for i := range h.central {
h.central[i].mcentral.init(spanClass(i))
}
h.pages.init(&h.lock, &memstats.gc_sys)
}