Linux内存管理(五):PTmalloc、TCmalloc、Jemalloc比较

1. 内存管理目标

2. 三种内存管理的比较


1. 内存管理目标

    内存管理的目的是实现了malloc(),free()以及一组其它的函数,以提供动态内存管理的支持。分 配器处在用户程序和内核之间,它响应用户的分配请求,向操作系统申请内存,然后将其返回给用户程序。

    为了保持高效的分配,分配器一般都会预先分配一块大于用户请求的内存, 并通过某种算法管理这块内存。来满足用户的内存分配要求,用户释放掉的内存也并不是立即就返回给操作系统,相反,分配器会管理这些被释放掉的空闲空间,以应对用户以后的内 存分配要求。也就是说,分配器不但要管理已分配的内存块,还需要管理空闲的内存块,当 响应用户分配要求时,分配器会首先在空闲空间中寻找一块合适的内存给用户,在空闲空间 中找不到的情况下才分配一块新的内存。

    由此可见,内存管理的核心目标主要有两点:

  • 高效的内存分配和回收,提升单线程或者多线程场景下的性能;
  • 减少内存碎片,包括内部碎片和外部碎片,提高内存的有效利用率;

2. 三种内存管理的比较

    ptmalloc 是基于 glibc 实现的内存分配器,它是一个标准实现,所以兼容性较好。pt 表示 per thread 的意思。当然 ptmalloc 确实在多线程的性能优化上下了很多功夫。由于过于考虑性能问题,多线程之间内存无法实现共享,只能每个线程都独立使用各自的内存,所以在内存开销上是有很大浪费的。

    tcmalloc 出身于 Google,全称是 thread-caching malloc,所以 tcmalloc 最大的特点是带有线程缓存,tcmalloc 非常出名,目前在 Chrome、Safari 等知名产品中都有所应有。tcmalloc 为每个线程分配了一个局部缓存,对于小对象的分配,可以直接由线程局部缓存来完成,对于大对象的分配场景,tcmalloc 尝试采用自旋锁来减少多线程的锁竞争问题。

    jemalloc 借鉴了 tcmalloc 优秀的设计思路,所以在架构设计方面两者有很多相似之处,同样都包含 thread cache 的特性。但是 jemalloc 在设计上比 ptmalloc 和 tcmalloc 都要复杂,jemalloc 将内存分配粒度划分为 Small、Large二个分类,并记录了很多 meta 数据,所以在空间占用上要略多于 tcmalloc,不过在大内存分配的场景,jemalloc 的内存碎片要少于 tcmalloc。

PTmallocTCmallocJemalloc
内存组织

(1)内存分配单位为chunk;

(2)小于64B的chunk放在fast bin中;

(3)64 - 512B放在small bin中;

(4)512B - 128 KB放large bin中;

(5)大于128KB不进行缓存;

(6)合并后的chunk放在unsorted bin中;

(1)内存有三层缓存:PageHeap、CentralCache和ThreadCache;

(2)0 - 256KB小对象放在中央缓存和线程缓存中,分为84个不同大小类别,中央缓存多个线程共享,线程级缓存每个线程私有;

(3)256KB - 1MB的中对象和1MB以上大对象放在PageHeap,每个page大小为8KB;

(1)小类区间为[8B, 14kb],共232个小类,每个类的大小并不都是2的次幂;

(2)大类区间为[16kB, 7EiB],page大小为4KB,从4 * page开始;

(3)内存分配单位为extent,每个extent大小为N * 4KB,一个 extent 可以用来分配一次 large_class 的内存申请,但可以用来分配多次 small_class 的内存申请。

分配流程fast bin —> small bins —> unsorted bin —> large bin —> top chunk —> 增加top chunk(sbrk/mmap) 或者 mmaped chunk;

(1)小对象:ThreadCache —> CentralCache —> PageHeap —> 内核;

(2)中对象和大对象:PageHeap —> 内核;

(1)小内存:cache_bin -> slab -> slabs_nonfull -> extents_dirty -> extents_muzzy -> extents_retained -> 内核

(2)大内存:extents_dirty -> extents_muzzy -> extents_retained -> 内核

多线程支持  没有线程级缓存,每个线程进行内存分配和释放时,需要对分配区进行加锁  每个线程拥有线程级缓存,当进行小对象分配和释放时,不用加锁处理  每个线程拥有线程级缓存tcache,进行小内存分配和释放时,不用加锁
优点它是一个标准实现,所以兼容性较好

(1)在多线程场景下,小对象内存申请和释放是无锁的,效率很高,中对象和大对象申请使用自旋锁;

(2)ThreadCache会阶段性的回收内存到CentralCache里,解决了ptmalloc2中分配区之间不能迁移的问题;

(3)占用更少的额外空间。例如,分配N个8字节对象可能要使用大约8N * 1.01字节的空间,即,多用百分之一的空间;

(1)采用多个arena来避免线程同步,多线程的分配是无锁的;

(2)细粒度的锁,比如每一个bin以及每一个extents都有自己的锁,并发度更高;

(3)使用了低地址优先的策略,来降低内存碎片化;

缺点

(1)管理长周期内存时,会导致内存爆增,因为与top chunk 相邻的 chunk 不能释放,top chunk 以下的 chunk 都无法释放;

(2)内存不能从一个分配区移动到另一个分配区, 就是说如果多线程使用内存不均衡,容易导致内存的浪费;

(3)如果线程数量过多时,内存分配和释放时加锁的代价上升,导致效率低下;

(4)每个chunk需要8B的额外空间,空间浪费大

  (1)对齐操作比PTmalloc多浪费一些内存,有点空间换时间;

(2)如果多个线程频繁分配大对象,对自旋锁的竞争会很激烈;

(1)arena之间的内存不可见,导致两个arena的内存出现大量交叉从而无法合并;

(2)大概需要2%的额外开销,tcmalloc是1%;

适用场景  不适合多线程场景和需要申请长周期内存,只适合线程数较少和申请短周期内存的场景适合多线程的场景适合多线程的场景,多线程并发度更好

性能对比图如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值