# runtime off-heap object allocator

runtime 中有很多内部的数据结构是与内存分配、垃圾回收相关的,这些数据结构的实例占用的内存空间会直接通过sysAlloc分配,或则是基于sysAlloc再封装一层或多层的分配器。

sysAlloc

上图列举了runtime中内部的一些数据结构所使用的分配器,总共有 3 个,分别是sysAlloc、persistent allocator 、fix allocator。 persistent allocator 管理的空间,从sysAlloc分配得到,而fix allocator管理的空间,从persistent allocator 分配

# Persistent allocator

persistent allocator 就是一个 sequential allocator,不过因为其分配出去的空间都不释放,所以叫做persistent allocator 。

persistent allocator 的数据结构非常简单,其维护了两个变量,一个是base,表示被管理的空间的起始地址,off (offset) 用于记录可用空间的起始地址与 base 的距离。persistent allocator 管理的空间(persistent chunk size)大小是 256 KB,当剩余空间不足以分配的时候,会调用sysAlloc 再申请一个 persistent chunk 。如果请求分配的大小大于64KB,会直接走sysAlloc

type persistentAlloc struct{
    base *notInHeap //notInHeap 是 指针类型
    off uintptr
}
1
2
3
4

persistent allocator

persistent allocator 会有一些空间浪费,包括对齐的浪费和尾部的浪费,如上图所示。每个P都有一个persistent allocator,因此分配不会出现竞争,所有 (指每个P)persistent chunk都会串在一起,每个新分配的persistent chunk 的起始部分会保留前一个persistent chunk 的首地址,而这个新创建的chunk 的首地址会存在一个全局变量 persistentChunks 中,通过原子操作进行更改。
把每个chunk 串起来的目的不是为了利用尾部的空闲空间,而是为了用与判断某个地址是否落在persistent chunk 中(垃圾回收的某一阶段需要判断指针写入的地址是否合法(写到非go管理的内存范围内),指向persistent chunk地址范围内的指针也是合法的一种)

# fixed size allocator

fixed-size allocator 用于分配runtime中常用的对象,比如内存管理相关的mspan。fixed-size allocator的设计与free-list allocator 类似,不过list 上的对象都是固定大小的。

type fixalloc struct {
    size uintptr
    first func(arg, p unsafe.Pointer)
    arg unsafe.Pointer
    list *mlink
    chunk uintptr
    nchunk uint32
    inuse uintptr
    stat *uint64
    zero bool
}

type mlink struct {
    next *mlink
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
字段 用途
size 分配器分配的对象的大小
first 一个函数,当分配器分配一个对象的时候,就会传入对象首地址,调用这个函数。唯一一个使用场景是当mspan对象被分配的时候,该函数把这个新分配的mspan加到全局的数组中
arg first 函数的第一个参数
list free list 的链表头
chunk 下一个空闲块的首地址
nchunk 剩余空闲地址块的大小
inuse 被使用的空间大小
stat 统计相关
zero 表示分配出去的空间是否需要清0

fixed size allocator
上图展示了fixed-size allocator 的工作原理,fixed-size allocator 的底层分配器是 persistent allocator ,fixed-size allocator 通过persistent allocator 分配16KB 大小的 chunk用于分配,当空间回收时,就把空闲的空间块挂在free list 中(指针存在空闲块中)。分配的时候,优先从free list 中分配,如果free list 为空,则从chunk处取出size大小的空闲块用于分配。如果free list 没有可用空间,nchunk也不满足分配需求,fixed-size allocator会再申请16KB空间,用于分配。