文章插图
go-memory-layout
图 7-10 Go 程序的内存布局
所有的 Go 语言程序都会在启动时初始化如上图所示的内存布局,每一个处理器都会被分配一个线程缓存 runtime.mcache 用于处理微对象和小对象的分配,它们会持有内存管理单元 runtime.mspan 。
每个类型的内存管理单元都会管理特定大小的对象,当内存管理单元中不存在空闲对象时,它们会从 runtime.mheap 持有的 134 个中心缓存 runtime.mcentral 中获取新的内存单元,中心缓存属于全局的堆结构体 runtime.mheap,它会从操作系统中申请内存 。
在 amd64 的 Linux 操作系统上,runtime.mheap 会持有 4,194,304 runtime.heapArena,每一个 runtime.heapArena 都会管理 64MB 的内存,单个 Go 语言程序的内存上限也就是 256TB 。
内存管理单元runtime.mspan 是 Go 语言内存管理的基本单元,该结构体中包含 next 和 prev 两个字段,它们分别指向了前一个和后一个 runtime.mspan:
type mspan struct { next *mspan prev *mspan ...}
串联后的上述结构体会构成如下双向链表,运行时会使用 runtime.mSpanList 存储双向链表的头结点和尾节点并在线程缓存以及中心缓存中使用 。文章插图
【Go 内存分配器的设计与实现】mspan-and-linked-list
图 7-11 内存管理单元与双向链表
因为相邻的管理单元会互相引用,所以我们可以从任意一个结构体访问双向链表中的其他节点 。
页和内存每个 runtime.mspan 都管理 npages 个大小为 8KB 的页,这里的页不是操作系统中的内存页,它们是操作系统内存页的整数倍,该结构体会使用下面的这些字段来管理内存页的分配和回收:
type mspan struct { startAddr uintptr // 起始地址 npagesuintptr // 页数 freeindex uintptr allocBits*gcBits gcmarkBits *gcBits allocCache uint64 ...}
- startAddr 和 npages — 确定该结构体管理的多个页所在的内存,每个页的大小都是 8KB;
- freeindex — 扫描页中空闲对象的初始索引;
- allocBits 和 gcmarkBits — 分别用于标记内存的占用和回收情况;
- allocCache — allocBits 的补码,可以用于快速查找内存中未被使用的内存;
文章插图
mspan-and-pages
图 7-12 内存管理单元与页
当用户程序或者线程向 runtime.mspan 申请内存时,该结构会使用 allocCache 字段以对象为单位在管理的内存中快速查找待分配的空间:
文章插图
mspan-and-objects
图 7-13 内存管理单元与对象
如果我们能在内存中找到空闲的内存单元,就会直接返回,当内存中不包含空闲的内存时,上一级的组件 runtime.mcache 可能会为该结构体添加更多的内存页以满足为更多对象分配内存的需求 。
状态运行时会使用 runtime.mSpanStateBox 结构体存储内存管理单元的状态runtime.mSpanState:
type mspan struct { ... statemSpanStateBox ...}
该状态可能处于 mSpanDead、mSpanInUse、mSpanManual 和 mSpanFree 四种情况 。当 runtime.mspan 在空闲堆中,它会处于 mSpanFree 状态;当 runtime.mspan 已经被分配时,它会处于 mSpanInUse、mSpanManual 状态,这些状态会在遵循以下规则发生转换:- 在垃圾回收的任意阶段,可能从 mSpanFree 转换到 mSpanInUse 和 mSpanManual;
- 在垃圾回收的清除阶段,可能从 mSpanInUse 和 mSpanManual 转换到 mSpanFree;
- 在垃圾回收的标记阶段,不能从 mSpanInUse 和 mSpanManual 转换到 mSpanFree;
跨度类runtime.spanClass 是 runtime.mspan 结构体的跨度类,它决定了内存管理单元中存储的对象大小和个数:
type mspan struct { ... spanclassspanClass ...}
Go 语言的内存管理模块中一共包含 67 种跨度类,每一个跨度类都会存储特定大小的对象并且包含特定数量的页数以及对象,所有的数据都会被预选计算好并存储在 runtime.class_to_size 和runtime.class_to_allocnpages 等变量中:
classbytes/objbytes/spanobjectstail wastemax waste1881921024087.50%2168192512043.75%3328192256046.88%44881921703231.52%5648192128023.44%68081921023219.07%..................6632768327681012.50%
推荐阅读
- 茶艺享受的是时间,茶艺根据不同的原则和方法的分类
- 茶油的营养价值,长期吃茶油的坏处
- 茶具的选购之配套用具,茶具的奇思妙想
- 科学管理Linux系统中的组与组成员
- 大红袍的产地,名茶大红袍的产地是哪
- svchost占用内存过高是怎么回事
- 基于MEC的边缘CDN业务调度方案及测试分析
- 茶香面包的做法,红茶面包棒的做法
- JavaScript的Array.flat函数深入探讨
- 买电脑、DIY电脑,你必须了解的避坑技能