前面断断续续的写了3篇关于Go语言内存分配器的文章,分别是Go语言内存分配器设计、Go语言内存分配器-FixAlloc、Go语言内存分配器-MSpan,这3篇主要是本文的前戏,其实所有的内容本可以在一篇里写完的,但内容实在太多了,没精力一口气搞定。本文将把整个内存分配器的架构以及核心组件给详细的介绍一下,当然亲自对照着翻看一下代码才是王道。

内存布局结构图

CacheMCentralHeap
runtime·mallocgcruntime·MCache_Allocruntime·MHeap_Alloc
runtime·mallocinit

初始化过程主要是在折腾mcache和mheap两个部分,而mcentral在实际逻辑中是属于mheap的子模块,所以初始化过程就没明确的体现出来,这和我绘制的结构图由两大块构造相对应。heap是所有底层线程共享的;而cache是每个线程都分别拥有一个,是独享的。在64位平台,heap从操作系统申请的内存地址保留区只有136G,其中bitmap需要8G空间,因此真正可申请的内存就是128G。当然128G在绝大多数情况都是够用的,但我所知道的还是有个别特殊应用的单机内存是超过128G的。

下面按小内存分配的处理路径,从cache到central到heap的过程详细介绍一下。

Cache

cache的实现主要在mcache.c源文件中,结构MCache定义在malloc.h中,从cache中申请内存的函数原型:

size
runtime·MCentral_AllocList
MemStats

cache的释放条件主要有两个,一是当某个内存块链表过长(>=256)时,就会截取此链表的一部分节点,返还给central;二是整个cache缓存的内存过大(>=1M),同样将每个链表返还一部分节点给central。

cache层在进行内存分配和释放操作的时候,是没有进行加锁的,也不需要加锁,因为cache是每个线程独享的。所以cache层的主要目的就是提高小内存的频繁分配释放速度。