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


针对小内存对象的分配 , Front-end的cache 会按照其大小将其映射到 60-80个size-classes(size-class是front-end 分配内存的粒度)中的一个 , 实际的内存分配会按照该大小对应的size-class 的大小进行分配 。比如12B 的对象会best-fit到16B 的size-class中 。设置这么多的size-class 是为了尽可能得降低内存的浪费 , 比如原本的内存分配粒度都是2的n次幂 , 那对于23字节的内存需求就需要分配一个32字节的内存区域 , 而在tcmalloc的size-class的配置中只需要分配24字节即可 。
对于大内存需求的对象来说 , 内存大小的需求超过256K , 则会直接从back-end 分配 。因此 , 这一部分的内存需求不会缓存再 front-end 以及 middle-end 中 , 由back-end的page-heap 进行管理 。对于大内存对象的实际内存分配会按照tcmalloc page size 进行对齐 。
如果要释放一个对象 , 编译期间如果能够知道这个对象的大小 , 编译器会直接告诉分配器这个对象的大小 。大多数的时候 , 编译期间并不清楚对象的大小 , 会从pagemap中查找这个对象 。如果这个对象是一个小内存对象 , 释放的时候会告诉front-end cache进行管理 , 如果是一个超过kMaxSize 的对象 , 则会直接释放给back-end 的 pageheap 。
4.1.3 Middle-endMiddle-end 的主要作用为 font-end 提供内存申请需求 , 并将空闲内存返回给 back-end 。Middle-end 对于每一个size-class , 都会有有一个各自的 transfer cache 和 central free list 。这一些caches 会有自己的互斥锁 , 所以对于这一些cache的访问 ,  因为锁粒度较低 , 则不会有过多的冲突 , 保证了访问的性能 。
当 front-end 请求内存或者释放内存的时候 , 会先到达transfer cache 。Transfer cache 会持有 一个数组指针进行内存的释放或者将新的内存对象填充进来并返回给font-end 。Transfer cache会将一个cpu/thread 释放的内存分配给另一个cpu/thread 对内存的需求 , 这个类似于内存池的内存对象流动在两个不同的cpu/threads 之间可以非常迅速 。
central free list通过 span 数据结构来管理内存 , 一个span可以管理一个或者多个tcmalloc page 。Font-end如果从central free list请求一个或者多个内存对象的时候 , central free list会从span中提取对应大小的对象 , 如果此时span 没有足够的pages返回 , 则会从back-end 请求更多的span 。
当内存对象返回给central free list , 则这一些对象会通过 pagemap 被映射到对应的span中进行管理 , 如果所有的对象都返回给span , 这个span就可以被释放给back-end 。
4.1.4 Back-endtcmalloc的back-end 主要有三个工作线程 , 分别负责管理未使用的大块内存区域 , 负责从 os 中获取内存 , 来满足其他组件的内存需求以及负责将其他组件不需要返回的内存 , 还给os 。还有两个后端组件:Legacy page heap和Huge page heap , 分别负责管理tcmalloc 的pages和管理大页内存的 pageheap 。Huge page heap可以用来提升应用程序大页内存的申请需求 , 提升页表缓存的命中率 。

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

文章插图
图片
legacy pageheap是一个数组的freelist , 统一管理可用内存页 。数组的每一个节点是一个free list , 也就是单链表 。一般这个数组的大小 k < 256 , 对于第k 个数组元素来说 , 它的链表中的每一个节点都管理了 k 个pages 。
如果想要申请 k 个pages , 则直接查找这个数组的第k 个元素 , 从free list中取一个节点即可 。如果这个free list是空的 , 那就查找下一个数组元素的free list,直到找到一个非空的free list 。如果还是失败 , 则直接mmap 获取内存 。
当一段连续的pages 被返回给了pageheap , 则会根据当前pages 是否能够形成一个连续的pages区域 , 然后串联这一些pages 并根据page数量 添加到对应的free list中 。
针对大页场景 , Huge page heap能够有效持有 hugepage 大小的内存块 , 需要的时候能够快速分配 , 不需要的时候也能在合理的内存占用情况下释放给操作系统 。
tcmalloc的back-end 拥有三个不同的cache 来管理大页内存的分配: