Go内存管理之TCMalloc

转自公众号:灰子学技术,原文链接:https://mp.weixin.qq.com/s/-b26YiHjL3V9CNvk0LZmEg

TCMalloc作为Go语言内存管理的核心算法,是理解和掌握Go的内存管理非常重要的一步,本章主要介绍TCMalloc的是什么样子的。

TCMalloc的概述

TCMalloc全称是Thread-Caching Malloc,是Google 开发的内存分配器,在不少项目中都有使用,例如在 Golang 中就使用了类似的算法进行内存分配。它具有现代化内存分配器的基本特征:对抗内存碎片、在多核处理器能够 scale。

1. TCMalloc相比glibc 2.3而言内存分配更快。

2.TCMalloc对于多线程程序而言,减少了锁机制,对于小对象而言,可以说没有锁的操作,对于大的对象而言的更加高效的自旋锁(spinlock)。

3.TCMalloc对小对象的处理空间效率更好。

1.TCMalloc 会给每一个线程分配一个属于线程本地的缓存(Thread Cache),这个本地缓存用于小对象(小于32K)的内存分配,在必要的时候,对象会从Central Heap(备注:这个是多个线程分享的,操作的时候需要做加锁、解锁处理)移动到Thread Cache。

2. 垃圾回收器(garbage collections)会周期性的将存储从Thread Cache(默认是2M)迁移到Central Heap,以便进行垃圾回收。

3.对于大的对象(大于32K)则是直接从Central Heap按照页面层次分配方式进行内存分配。

小对象的分配

每一个小的对象会对应于已经分配好的170类大小簇,而这170个大小簇是按照大小分配的,例如:8、16、32、48...256KB。对于分配的对象与这些类簇的对应关系是采用取整的方式对应起来的,例如:961 到 1024 bytes都会对应与1024这个类簇。

每一个类簇后面都会跟着一个freelist,freelist其实是一个链表,它里面的元素是按照类簇的大小来分配好的,该元素的前8个字节是作为指针,用于指向后续的节点,将整个节点串起来。如下图所示:

小对象分配的流程如下所示:

step 1: 首先,根据对象的大小找到对应的类簇,例如是32字节的位置。

step 2: 从这个线程的类簇中对应的freelist中查找,如果这个freelist中还有没有被分配的元素,则选择该元素并返回这个对象(备注:是从头到尾的顺序来查找)。

step 3: 如果这个类簇对应的freelist已经都被分配完了,1)先从central Heap中取出一些对象给thread Cache;2) 然后再从这个freelist中按照step 2中的步骤来分配对象。

step 4: 如果central Heap中的freelist也是空的,那么会按照下面的方式来操作:1)先分配一个运行页给central Heap;  2) 把这个运行页按照对应的class-size分成一组对象,并且将这些对象放到central Heap的freelist下面; 3) 按照step 3的方式进行操作,就会最后分配成功一个对象出去。

大对象的分配

大对象指的是大于32K的对象,它们的分配是在central Heap中完成的。对于大对象来说,是根据页面来大小来定义的,一般一个页面是8K,如果对象是大于一个页面的,就会被分配几个页面来完成数据分配。如下图所示:

多个连续的page会组成一个span,在span中会记录起始页编号,以及page的数量,分配时大对象直接分配span,小对象会从span中分配对象。page 和span的对应关系如下图所示,从图中可以看出span a对应的是2个page, span b对应的是一个page, span c对应的是5个page,span d对应的是3个page。

对于大对象而言,是通过pageHeap来进行管理的,结构如下所示:

大对象的分配流程如下所示:

step 1: 首先判断这个大对象需要占几页,假设是n,那么会从n page对应的freelist查找有没有可以分配的span,有的话直接返回。

step 2: n page 对应的span没有的话,会从n+1 page对应的span里面查找有没有合适的,有的话,将对应的span切分成n 和 1 page的span,将n 页的span返回,将1页的span放到1 page中的freelist中。

step 3: 如果 从1到128page都找不到的话,从large page中查找,这个操作要复杂些。

step 4: large page还是找不到的话,会向操作系统申请 128 page, 并同时将需要的n page的span返回,剩余的span 给其他的 page,以方便下次使用。

大对象的释放流程如下:

step 1: 根据当前要释放的span去查看它的左右邻居,如果是空闲的,则将它们从freelist中移除,然后合并为同一个span,然后挂在freelist中。

step 2: 检查是否需要释放给操作系统,需要的话,就释放给操作系统。

参考资料:

TCMalloc : Thread-Caching Malloc: http://goog-perftools.sourceforge.net/doc/tcmalloc.html

tcmalloc 介绍: http://legendtkl.com/2015/12/11/go-memory/

tcmalloc原理剖析(基于gperftools-2.1):http://gao-xiao-long.github.io/2017/11/25/tcmalloc/

图解 TCMalloc: https://zhuanlan.zhihu.com/p/29216091

灰子学技术:

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章