TCMalloc详解
本文部分内容是结合参考其他文章已经tcmalloc官方文档。
TCMalloc
TCMalloc 是 Google 对 C 的 malloc() 和 C++ 的 operator new 的自定义实现,用于在我们的 C 和 C++ 代码中进行内存分配。 TCMalloc 是一种快速、多线程的 malloc 实现。
TCMalloc为每个线程分配了缓存,这个缓存是线程私有的,可以减少多线程程序竞争。对于小对象的内存分配,首先会去请求线程缓存,不用加锁,如果缓存不能满足的话,需要去向后面的内存存储结构中获取,此时需要加锁获取,因为其他线程可能正在获取内存空间,但是大部分情况下线程缓存就可以满足内存请求,所以几乎不需要锁。对于大对象的内存分配,TCMalloc尝试着使用细粒度和高效的自旋锁。另外一个TCMalloc的好处是小对象内存分配效率高。例如,分配n个8 byte的对象时,使用大约8n * 1.01byte的空间,只有百分之一的空间浪费。ptmalloc2分配内存的方法为每个对象使用一个4 byte的标头,并且将大小四舍五入为8 byte的倍数,最终使用16n byte。
虚拟内存
有一个结论需要提一下,我们的进程是运行在虚拟内存上的,图示如下:
-
对于我们的进程而言,可使用的内存是连续的
-
安全,防止了进程直接对物理内存的操作(如果进程可以直接操作物理内存,那么存在某个进程篡改其他进程数据的可能)
-
虚拟内存和物理内存是通过MMU(Memory Manage Unit)映射的(感兴趣的可以研究下)
-
等等(感兴趣的可以研究下)
所以,以下文章我们所说的内存都是指虚拟内存。
TCMalloc架构
下面这幅图粗略的介绍了TCMalloc的内部结构:
我们可以将TCMlloc分为三部分,front-end, middle-end, back-end。 它们的职责分别是:
- Front-end是一个缓存,提供内存快速分配和重分配内存给应用程序的功能。它主要有2部分组成:Per-thread cache和Per-CPU cache。这里只聊 Per-thread模式。
- Middle-end负责给front-end提供缓存。当front-end缓存不足时,首先从middle-end中获取。它由Central free list组成。
- Back-end负责从系统获取内存。当middle-end中的内存不足时,从back-end中获取。它主要设计page heap的内容。
注意一下,front-end可以在per-cpu或者legacy per-thread模式下运行,后端可以支持hugepage aware pageheap或者legacy pageheap。
TCMalloc Front-end
Front-end
可以处理特定大小的内存分配请求。Front-end
有一个线程缓存,可用于分配或保存空闲内存。这个缓存一次只能被一个线程访问,所以它不需要任何锁,因此大多数分配和释放都很快。
如果Front-end
具有适当大小的内存缓存,可以直接从缓存中为程序分配对象。如果该特定大小的内存块为空,则Front-end
将从Middle-end
请求一批内存以重新填充缓存。中端包括 CentralFreeList
和 TransferCache
。
如果Middle-end
可用的内存空间耗尽,或者请求的大小大于Front-end
缓存的最大大小,则请求会去Back-end
中获取内存,或者重新填充Middle-end
的内存。后端也称为 PageHeap。
TCMalloc 前端有两种实现:
-
最初它支持对象的每线程缓存(因此得名
Thread-local malloc