Malloc技术原理解析以及在转转搜索业务上的实践( 五 )

3.4 部分参数解析MALLOC_ARENA_MAX(线程内存池的最大数量):通过将MALLOC_ARENA_MAX设置为较小的值 , 可以减少arena的数量 , 从而降低内存碎片的可能性 。然而 , 这可能会导致高并发环境中的锁争用问题 , 从而对性能产生负面影响 。因此 , 需要在性能和内存利用之间找到一个平衡点 。
此外 , ptmalloc2默认会根据内存需求的动态情况来调整mmap分配的阈值 , 以便更有效地利用内存缓冲池设置,但是设置M_TRIM_THRESHOLD , M_MMAP_THRESHOLD , M_TOP_PAD 和 M_MMAP_MAX 中的任意一个就可以固定分配阈值为128K , 这样超过128K的内存分配请求都不会进入ptmalloc的buffer池而是直接走mmap分配和munmap回收(性能上会有损耗) 。
3.5 特性分析

  • ptmalloc2使用了多arena 来分配内存 , 虽然增加了内存碎片 , 但却提升了内存分配效率;
  • 后分配的内存先释放 , 因为 ptmalloc 收缩内存是从 top chunk 开始,如果与 top chunk 相邻的 chunk 不能释放, top chunk 以下的 chunk 都无法释放;
  • 多线程锁开销大 , 需要避免多线程频繁分配释放;
  • 内存从thread的areana中分配 ,  内存不能从一个arena移动到另一个arena ,  就是说如果多线程使用内存不均衡 , 容易导致内存的浪费 。比如说线程1使用了300M内存 , 完成任务后glibc没有释放给操作系统 , 线程2开始创建了一个新的arena ,  但是线程1的300M却不能用了;
  • 每个chunk至少8字节的开销很大;
  • 不定期分配长生命周期的内存容易造成内存碎片 , 不利于回收 。64位系统最好分配32M以上内存 , 这是使用mmap的阈值 。
4 tcmallocTCMalloc 全称 Thread-Caching Malloc , 即线程缓存的 malloc , 是 Google 开发的内存分配器 , 在不少项目中都有使用 , 目前已经在chrome、safari等知名软件中运用 。
4.1 整体架构tcmalloc的架构大体分为三个部分:
  • Front-end用来为应用提供快速分配以及释放内存的需求 。可以根据参数的配置调整为Per-cpu mode 和 per-thread mode两种模式 , 后面会细讲;
  • Middle-end 由 CentralFreeList 和 TransferCache 两部分组成 , 负责重新填充Front-end的Cache , 并将空闲内存返回给 back-end;
  • back-end 负责管理从操作系统获取的内存 , 支持大内存和小内存的pageheap 管理 。
下面将对内存组织形式和三个核心组成部分进行详细讲解 , 并说明在内存的分配和释放过程中各个部分的机制 。
4.1.1 Pagemap 和 Spanstcmalloc 管理的堆内存会在编译期间确定一个page-size , 并将这么多内存映射为对应size的一个个page 。一系列正在被使用的pages 可以被一个span 对象描述 , 一个span 对象可以管理一个大的内存对象 , 也可以按照size-class 管理多个小对象 。
pagemap 则是用来查找一个内存对象属于哪一个span的 , 或者申请一个指定size-class 的内存对象 , pagemap 根据32位还是64位是一个 2层或者3层的 radix-tree 。下图展示了一个两层的 page-map 如何管理 span的 , 其中 span A 管理了2个page , span B 管理了三个page 。
Malloc技术原理解析以及在转转搜索业务上的实践

文章插图
图片
Span 这个数据结构在middle-end中用来管理回收的内存对象 , 在back-end 用来管理 对应大小的pages , 负责给central-list 提供对应大小的span 。
4.1.2 Front-endFront-end 提供了Cache , 能够缓存一部分内存用来分配给应用  , 也能够持有应用释放的内存 。其同一时刻只能由一个线程访问 , 所以本身不需要任何的锁 , 这也是多线程下内存分配释放高效的原因 。如果Front-end 持有的内存大小足够 , 其能够满足应用线程任何内存需求 。如果持有的内存为空了 , 那它会从 middle-end 组件请求一批内存页进行填充 。如果用户请求的内存大小超过了front-end 本身能缓存的大小(大内存需求) , 或者middle-end 缓存的内存页也被用尽了 , 那这个时候会直接让从back-end分配内存给用户 。


推荐阅读