内存分配原理
一、基本情况
- 内存分配器有glibc提供的ptmalloc2,谷歌提供的tcmalloc,脸书提供的jemalloc
- golang中提供了内存分配器,原理与tcmalloc类似,简单说维护一块大的全局内存,每个线程维护一块小的私有内存,私有内存不足再从全局申请
- 内存分配与GC(垃圾回收)有密切关系,所以,了解GC前需要了解内存分配的原理
二、基本概念
- 为了golang自主管理内存,先向系统申请一块内存,然后将内存切割成小块,通过一定的内存分配算法管理内存。
- 申请的内存划分为三个部分,spans,bitmap,arena.其中arena为堆区,程序中需要的内存从这里分配。
- spans,bitmap为了管理arena而存在。arena大小为512G,为了管理arena区域,区域划分成一个个page,每个page 8k,共512G/8k
- spans 区域存放span指针,每个指针对应一个或多个page,所以span区域的大小为 512G/8k指针大小8byte=512M
- bitmap区域大小通过arena计算出来,一般用于GC垃圾回收
三、 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 32
// 5 64 8192 128 0
// 6 80 8192 102 32
// 7 96 8192 85 32
// 8 112 8192 73 16
// 9 128 8192 64 0
// 10 144 8192 56 128
// 11 160 8192 51 32
// 12 176 8192 46 96
// 13 192 8192 42 128
// 14 208 8192 39 80
// 15 224 8192 36 128
// 16 240 8192 34 32
// 17 256 8192 32 0
// 18 288 8192 28 128
// 19 320 8192 25 192
// 20 352 8192 23 96
// 21 384 8192 21 128
// 22 416 8192 19 288
// 23 448 8192 18 128
// 24 480 8192 17 32
// 25 512 8192 16 0
// 26 576 8192 14 128
// 27 640 8192 12 512
// 28 704 8192 11 448
// 29 768 8192 10 512
// 30 896 8192 9 128
// 31 1024 8192 8 0
// 32 1152 8192 7 128
// 33 1280 8192 6 512
// 34 1408 16384 11 896
// 35 1536 8192 5 512
// 36 1792 16384 9 256
// 37 2048 8192 4 0
// 38 2304 16384 7 256
// 39 2688 8192 3 128
// 40 3072 24576 8 0
// 41 3200 16384 5 384
// 42 3456 24576 7 384
// 43 4096 8192 2 0
// 44 4864 24576 5 256
// 45 5376 16384 3 256
// 46 6144 24576 4 0
// 47 6528 32768 5 128
// 48 6784 40960 6 256
// 49 6912 49152 7 768
// 50 8192 8192 1 0
// 51 9472 57344 6 512
// 52 9728 49152 5 512
// 53 10240 40960 4 0
// 54 10880 32768 3 128
// 55 12288 24576 2 0
// 56 13568 40960 3 256
// 57 14336 57344 4 0
// 58 16384 16384 1 0
// 59 18432 73728 4 0
// 60 19072 57344 3 128
// 61 20480 40960 2 0
// 62 21760 65536 3 256
// 63 24576 24576 1 0
// 64 27264 81920 3 128
// 65 28672 57344 2 0
// 66 32768 32768 1 0
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表中的对象大小,也即块大小
}
type mcache struct {
alloc [67*2]*mspan // 按class分组的mspan列表
}
type mcentral struct {
lock mutex //互斥锁
spanclass spanClass // span class ID
nonempty mSpanList // non-empty 指还有空闲块的span列表
empty mSpanList // 指没有空闲块的span列表
nmalloc uint64 // 已累计分配的对象个数
}
type mheap struct {
lock mutex
spans []*mspan
bitmap uintptr //指向bitmap首地址,bitmap是从高地址向低地址增长的
arena_start uintptr //指示arena区首地址
arena_used uintptr //指示arena区已使用地址位置
central [67*2]struct {
mcentral mcentral
pad [sys.CacheLineSize - unsafe.Sizeof(mcentral{})%sys.CacheLineSize]byte
}
}