Go语言的整个内存管理子系统主要由两部分组成——内存分配器和垃圾收集器(gc)。十一小长假期为了避开我泱泱大国的人流高峰,于是在家宅了3天把Go语言的内存分配器部分的代码给研究了一番,总的来说还是非常酷的,自己也学到了不少的东西,就此记录分享一下。整个内存分配器完全是基于Google自家的tcmalloc的设计重新实现了一遍,因此,想看看Go语言的内存分配器实现的话,强烈建议先读一读tcmalloc的介绍文档,然后看看Go runtime的malloc.h源码文件的注释介绍,这样基本就大概了解Go语言内存分配器的设计了。
Go的内存分配器主要也是解决小对象的分配管理和多线程的内存分配问题。(后面提到的内存分配器都是指代的Go语言实现的内存分配器)。内存分配器以32k作为对象大小的定夺标准,小于等于32k的内存对象一律视为小对象,而大于32k的对象就是大对象了。为何是32k作为分界线呢?这个我也不知道,我觉得这是一个经验值吧,如果你知道有其他更加科学的理由,麻烦告知我一下。
内存分配器会将分配的小对象使用一个cache组件给缓存起来,只要是分配小对象就先到cache中查询一下,有空闲的内存就直接返回使用,不用向操作系统申请内存。内存分配器的这个cache组件可能同时存在多个,也就是每个实际线程都会有一个cache组件,这样一来,从cache里查询、获取空闲内存的时候就不需要加锁了,每次小对象的申请直接访问本线程对应的cache即可。我们再写程序的时候,其实绝大多数的内存申请都是小于32k的,属于小对象,因此这样的内存分配全部走本地cache,不用向操作系统申请显然是非常高效的。
CacheCentralHeap
Heap
内存分配器的实现我需要拆成多篇文章来写,没精力一口气写完,其实按组件拆开写也方便阅读嘛。后面的文章将陆续写完各个核心组件。
***
题外话,在基础系统软件的世界里,内存管理是一个永恒的话题,所以存在tcmalloc和jemalloc这类非常优秀的内存分配器实现。据说,jemalloc在cpu核数较多的情况下,性能还要优于tcmalloc,但估计它们之间是不相伯仲的,主体设计都差不多。jemalloc也是纯C代码,应该是非常值得一看的。不知道为何,现在对C++项目,总有研究拖延症,没有强烈的动力去第一时间看源码。