目录

go内存分配(上)

概述

为了方便自主管理内存,做法便是先向系统申请一块内存,然后将内存切割成小块,通过一定的内存分配算法管理内存。

./pic1.jpg

预申请的内存划分为spans、bitmap、arena三部分。其中arena即为所谓的堆区,应用中需要的内存从这里分配。 其中spans和bitmap是为了管理arena区而存在的。

arena的大小为512G,为了方便管理把arena区域划分成一个个的page,每个page为8KB,一共有512GB/8KB个页;

spans区域存放span的指针,每个指针对应一个page,所以span区域的大小为(512GB/8KB)*指针大小8byte = 512M

bitmap区域大小也是通过arena计算出来,不过主要用于GC。

./pic2.png

spans

span是内存管理的基本单位,每个span用于管理特定的class对象, 每个class都代表一个固定大小的对象,跟据对象大小,span将一个或多个页拆分成多个块进行管理。

换句话说为了更好的应对不同大小的对象的分配,有了class的概念,一共67中class,每种class所拥有的page数量是不同的。

src/runtime/mheap.go:mspan定义了其数据结构:

type mspan struct {
	next *mspan			//链表前向指针,用于将span链接起来
	prev *mspan			//链表前向指针,用于将span链接起来
	startAddr uintptr // 起始地址,也即所管理页的地址
	npages    uintptr // 管理的页数
	
	nelems uintptr // 块个数,也即有多少个块可供分配

	allocBits  *gcBits //分配位图,每一位代表一个块是否已分配

	allocCount  uint16     // 已分配块的个数
	spanclass   spanClass  // class表中的class ID

	elemsize    uintptr    // class表中的对象大小,也即块大小
}

以class 10为例,span和管理的内存如下图所示:

./pic3.jpg

chache

有了管理内存的基本单位span,还要有个数据结构来管理span,这个数据结构叫mcentral,各线程需要内存时从mcentral管理的span中申请内存,为了避免多线程申请内存时不断的加锁,Golang为每个线程分配了span的缓存,这个缓存即是cache。

central

cache作为线程的私有资源为单个线程服务,而central则是全局资源,为多个线程服务,当某个线程内存不足时会向central申请,当某个线程释放内存时又会回收进central。

heap

central内存不够时,从heap申请。

内存分配过程

以申请size为n的内存为例,分配步骤如下:

获取当前线程的私有缓存mcache 跟据size计算出适合的class的ID 从mcache的alloc[class]链表中查询可用的span 如果mcache没有可用的span则从mcentral申请一个新的span加入mcache中 如果mcentral中也没有可用的span则从mheap中申请一个新的span加入mcentral 从该span中获取到空闲对象地址并返回

总结

Golang程序启动时申请一大块内存,并划分成spans、bitmap、arena区域 arena区域按页划分成一个个小块 span管理一个或多个页 mcentral管理多个span供线程申请使用 mcache作为线程私有资源,资源来源于mcentral