·go语言内置运行时,更好的管理内存使用,不用每次都调用系统分配
go语言运行时内存分配算法为 Thread-CachingMalloc。核心思想就是把内存分为多级管理,从而降低锁的粒度。
go内存分配将堆内存采用二级分配的方式进行管理。每个线程自行维护一块独立的内存池。进行内存池分配时优先从该内存中分配。当内存池不足时才会想全局内存池申请。以避免不同线程对全局内存池的频繁竞争。
基础概念
GO语言运行时会向操作系统申请一块内存。(虚拟内存空间,并不是真正的内存分配)。
申请到的内存块被分配了三个区域,在X64上分别是512MB,16GB,512GB大小。
arena区域就是我们所谓的堆区,Go动态分配的内存都是在这个区域,它把内存分割成 8KB大小的页,一些页组合起来称为 mspan。
bitmap区域标识 arena区域哪些地址保存了对象,并用 4bit标志位表示对象是否包含指针、 GC标记信息。 bitmap中一个 byte大小的内存对应 arena区域中4个指针大小(指针大小为 8B )的内存,所以 bitmap区域的大小是 512GB/(48B)=16GB。
spans区域存放 mspan(也就是一些 arena分割的页组合起来的内存管理基本单元,后文会再讲)的指针,每个指针对应一页,所以 spans区域的大小就是 512GB/8KB8B=512MB。除以8KB是计算 arena区域的页数,而最后乘以8是计算 spans区域所有指针的大小。创建 mspan的时候,按页填充对应的 spans区域,在回收 object时,根据地址很容易就能找到它所属的 mspan。
内存管理单元
mspan:go中内存管理的基本单元。 mspan是一个包含起始地址、mspan规格、页的数量等内容的双端链表。
内存分配超过32KB-32768就算是大对象了。 大对象直接由堆内存分配, 而小对象都要通过mspan来分配。
mspan 结构体的内容 TODO
/usr/local/go/src/runtime/mheap.go
内存管理组件
内存分配有内存分配器完成,分配器由3钟组件构成 mcache、mcentral、 mheadp
mcache
mcentral
mheap
内存分配流程
Go的内存分配器在分配对象时,根据对象的大小,分成三类:小对象(小于等于16B)、一般对象(大于16B,小于等于32KB)、大对象(大于32KB)。
分配流程:
32KB 的对象,直接从mheap上分配;
- <=16B 的对象使用mcache的tiny分配器分配;
(16B,32KB] 的对象,首先计算对象的规格大小,然后使用mcache中相应规格大小的mspan分配;
Go在程序启动时,会向操作系统申请一大块内存,之后自行管理。
- Go内存管理的基本单元是mspan,它由若干个页组成,每种mspan可以分配特定大小的object。
- mcache, mcentral, mheap是Go内存管理的三大组件,层层递进。mcache管理线程在本地缓存的mspan;mcentral管理全局的mspan供所有线程使用;mheap管理Go的所有动态分配内存。
- 极小对象会分配在一个object中,以节省资源,使用tiny分配器分配内存;一般小对象通过mspan分配内存;大对象则直接由mheap分配内存。