mspan
定义
Go 语言内存管理的基本单元
mspan之间形成双向链表结构,每个mspan 都管理 n 个大小为 8KB 的页
每个mspan都有一个spanClass,代表是了那种跨度类型的span
结构
startAddr 和 npages — 确定该结构体管理的多个页所在的内存,每个页的大小都是 8KB;
freeindex — 扫描页中空闲对象的初始索引;
allocBits 和 gcmarkBits — 分别用于标记内存的占用和回收情况;
allocCache — allocBits 的补码,可以用于快速查找内存中未被使用的内存
67种spanClass,0是特殊的代表大于32KB的spanClass
mcache
定义
是 Go 语言中的线程缓存,它会与线程上的处理器一一绑定,主要用来缓存用户程序申请的微小对象。每一个线程缓存都持有 68 * 2 个 runtime.mspan,这些内存管理单元都存储在结构体的 alloc 字段中
初始化
初始化时是不包含 mspan ,只有当用户程序申请内存时才会从上一级组件获取新的 mspan 满足内存分配的需求。
替换
当 mcache 中没有找到可用指定跨度的 mspan ,会从 mcentral 中的可用 mspan 将其替换
tiny分配器
组成
tiny: 指向堆中的一片内存,tinyOffset
tinyOffset: 是下一个空闲内存所在的偏移量
local_tinyallocs: 会记录内存分配器中分配的对象个数
默认管理16B以下的对象
使用场景
运行时将小于 16 字节的对象划分为tiny对象,它会使用线程缓存上的tiny分配器提高微对象分配的性能,我们主要使用它来分配较小的字符串以及逃逸的临时变量。tiny分配器可以将多个较小的内存分配请求合入同一个内存块中,只有当内存块中的所有对象都需要被回收时,整片内存才可能被回收.
思考
你认为mcache时跟谁拥有?(go 有GM模型跟GPM模型),是在M上?还是在P上?或者G上?为什么在xxx上捏?
为什么这里需要分scan跟noscan?
mcentral
定义
1. 每个 mcentral 都是一种跨度类型的 mspan的全局后备资源,因此共有 136 个 mcentral;<br>2. 访问中心缓存中的内存管理单元需要使用互斥锁
组成
spanclass: 代表这个mcentral是那种类型的mspan
partial: 代表里面有空闲对象的mspan<br>
full: 代表了没有空闲对象的mspan
内存分配过程
<ol><li>调用 runtime.mcentral.partialSwept从清理过的、partial中查找可以使用的msapn;</li><li>调用 runtime.mcentral.partialUnswept从未被清理过的、partial中查找可以使用的内存管理单元</li><li>调用 runtime.mcentral.fullUnswept 获取未被清理的、full中获取内存管理单元并通过 runtime.mspan.sweep 清理它的内存空间</li><li>调用 runtime.mcentral.grow 从堆中申请新的内存管理单元;</li><li>更新内存管理单元的 allocCache 等字段帮助快速分配内存;</li></ol>
mheap
定义
runtime.mheap 是内存分配的核心结构体,Go 语言程序会将其作为全局变量存储,而堆上初始化的所有对象都由该结构体统一管理,该结构体中包含两组非常重要的字段,其中一个是全局的中心缓存列表 central,另一个是管理堆区内存区域的 arenas 以及相关字段