内存管理
对于内置runtime system的编程语言,通常会抛弃传统的内存分配方式,改为自主管理内存。这样可以完成类似预分配、内存池、垃圾回收等操作,以避开频繁地向操作系统申请、释放内存,产生过多的系统调用而导致的性能问题。
golang的runtime system同样实现了一套内存池机制,接管了所有的内存申请和释放的动作。
核心数据结构
这里主要涉及的是golang 堆内存的管理,stack内存的管理相对来说比较简单,因为stack中的对象生命周期有限,随着退栈所有对象都会被释放。
golang 堆内存的主要数据结构:
- mheap: mheap管理向os申请、释放、组织mspan;
- mcentral: mcentral按照自己管理的块大小将mspan分配给mcache;
- mspan: mspan是数据的实际存储区域,按照mcentral管理的块规格(class)被切分成小块。
- mcache: mcache中的alloc管理不同规格(class)的mspan:规格相同的mspan被链接到同一个链表中。
内存分配算法
为提高内存分配的效率,针对不同的对象大小,golang runtime system采用了不同的分配策略。
tiny object allocation
对于小于maxTinySize(16B)字节对象的内存分配请求。go采取了将小对象合并存储的解决方案。在线程的本地缓存中维护了专门的区域(mcache.tiny)来存储tiny object。
在请求tiny object内存分配的时候,首先查看mcache.tiny的剩余空间是否能够满足tiny object对象的分配。如果足够则直接返回;如果mcache.tiny的内存不够分配,则需要向上一届mcache, mcentral, mheap, system依次申请内存,然后再分配。
1 |
|
big object allocation
对于32KB的对象,跳过mcache和mcentral,直接在mheap上进行分配。
1 | var s *mspan |
small object allocation
对于 >=16B && <= 32KB的对象
- 如果 mcache 对应的 size class 的 span 已经没有可用的块,则向 mcentral 请求。
- 如果 mcentral 也没有可用的块,则向 mheap 申请,并切分。
- 如果 mheap 也没有合适的 span,则向操作系统申请。
1 | // 找到合适的sizeclass(规格),这里的处理方式很巧妙 |