tcmalloc原理剖析

少于 1 分钟读完

Tcmalloc是谷歌开发的一个内存分配器,管理堆内存,主要影响malloc和free,用于降低频繁分配、释放内存造成的性能损耗,并且有效地控制内存碎片。glibc中的内存分配器是ptmalloc2,tcmalloc号称要比它快。一次malloc和free操作,ptmalloc需要300ns,而tcmalloc只要50ns。同时tcmalloc也优化了小对象的存储,需要更少的空间。tcmalloc特别对多线程做了优化,对于小对象的分配基本上是不存在锁竞争,而大对象使用了细粒度、高效的自旋锁(spinlock)。分配给线程的本地缓存,在长时间空闲的情况下会被回收,供其他线程使用,这样提高了在多线程情况下的内存利用率,不会浪费内存,而这一点ptmalloc2是做不到的。

整体架构

system

Tcmalloc包括线程内存和中心堆两部分。对于每个线程都有一个Thread Cache,它负责分配每个线程所需要的小内存。这样做的好处就是线程之间分配小内存不存在锁竞争,提高了小内存的分配效率。对于大块的内存,所有线程都直接找中心堆分配,为了应对并发,大对象使用了细粒度高效的自旋锁。

小内存分配

system

Thread Cache的主要数据结构就是上图Free List,总共有60多个class,每个class管理相同大小的内存块。不同class管理的内存块大小不同,依次按8字节、16字节、32字节等间隔开。在内存分配的时候,会找到最小复合条件的,比如833字节到1024字节的内存分配请求都会分配一个1024大小的内存块。如果这些分配器的剩余内存不够了,会向中心堆申请一些内存,打碎以后填入对应分配器中。 这里存在一个问题就是每个class管理的内存块多少的问题。在线程内存池每次从中央堆申请内存的时候,分配多少内存也直接影响分配性能。申请地太少会导致频繁访问中心堆,也就会频繁加锁,而申请地太多会导致内存浪费。在tcmalloc里,这个每次申请的内存量是动态调整的,调整方式使用了类似把tcp窗口反过来用的慢启动(slow start)算法调整。

大内存的分布

system

对于大内存分配(大于8个分页, 即32K),Tcmalloc直接在中央堆里分配。中央堆的内存管理是以分页为单位的,同样按大小维护了256个空闲空间链表,前255个分别是1个分页、2个分页到255个分页的空闲空间,最后一个是更多分页的小的空间。这里的空间如果不够用,就会直接从系统申请了。

分页管理Spans

system

Tcmalloc使用Span来管理内存分页,一个span可以包含几个连续分页。一个span的状态只有未分配(这时候在空闲链表中),作为大对象分配,或作为小对象分配(这时候span内记录了小对象的class size)。在32位系统中,Span分为两级由中央分配器管理。第一级有2^5个节点,第二级是2^15个。32位总共只能有2^20个分页(每个分页4KB = 2^12)。

内存回收

当内存被释放时,我们会计算该内存所在的页号并找到对应的Span。Span便可确定该内存是大内存还是小内存。如果是小内存并且可以插入到free list中,我们便把它插入到当前thread cache中free list的合适地方。如果是小内存但但大于2M就会被垃圾回收机制回收到中心堆。 如果是大内存,我们便会获取它对应的页号,并尝试和左右空闲的页合并。然后生成新的Span管理这块内存并放到页管理器中。

垃圾回收

垃圾回收机制可以保证每个Thread Cache的大小合适,如果一个Thread Cache有太多的空闲内存就会被收集,从而保证每个Thread Cache都可以获得相应的内存。垃圾会后机制只在内存释放时才会被触发。

分类:

更新时间: