go的内存分配器就是维护一块大的全局内存,每个线程(go中为P)维护一块小的私有内存,私有内存不足再从全局申请。
1 基础概念
做法是:先向系统申请一块内存,然后将内存切割成小块,通过一定的内存分配算法管理内存。以64为系统为例,Golang程序启动时会向系统申请的内存如下图所示:
预申请的内存划分为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
2 span
span是用于管理arena页的关键数据结构,每个span中包含1个或多个连续页,为了满足小对象分配,span中的一页 会划分更小的粒度,而对于大对象比如超过页大小,则通过多页实现
2.1 class
根据对象大小,划分了一系列class,每个class都代表一个固定大小的对象,以及每个span的大小。如下表所示
// class bytes/obj bytes/span objects waste bytes
// 1 8 8192 1024 0
// 2 16 8192 512 0
// 3 32 8192 256 0
// 4 48 8192 170 0
// 5 64 8192 128 0
// 6 80 8192 102 0
// 7 96 8192 85 0
// 8 112 8192 73 0
- class:class ID,每个span结构中都有一个class ID, 表示该span可处理的对象类型
- bytes/obj:该class代表对象的字节数
- bytes/span:每个span占用堆的字节数,也即页数*页大小
- objects:每个span可分配的对象