首先列一个层级架构:
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)
}