ptmalloc2 为了支持多线程而设计的核心架构——Arena。它可以说是整个分配器的“战场”和“资源池”。
可以把 Arena 理解为一个完全独立的内存“王国”,每个王国都有自己的堆内存、自己的各种 Bin(空闲链表)、自己的锁,以及自己的 Top Chunk。多线程环境下,不同线程可以同时操作不同的 Arena,从而实现并发分配,减少锁竞争。
1. 什么是 Arena?为什么需要它?
在早期的dlmalloc中,整个进程只有一个堆,所有线程的malloc/free操作都需要竞争一个全局锁。这在多核时代显然会成为性能瓶颈。
Arena 的出现就是为了解决这个问题。它的核心思想是:
资源隔离:每个 Arena 管理自己独立的内存区域(一个或多个连续的堆段)。
并发分配:线程可以尝试获取一个专属的 Arena,之后的内存分配和释放主要在这个 Arena 内进行,从而减少了与其他线程竞争全局锁的概率。
数量限制:Arena 的数量并不是无限的。在 64 位系统上,通常为CPU 核心数的 8 倍。这是为了在并发性能和内存开销之间取得平衡。
2. Arena 的类型
ptmalloc2 管理着两种类型的 Arena:
主 Arena(Main Arena):
这是进程启动时由 Glibc 自动创建的 Arena。
它使用的堆内存是通过
brk/sbrk系统调用扩展的,即传统的 program break 方式。主 Arena 只有一个,其结构体是全局变量。
非主 Arena(Thread Arena):
当主 Arena 被占用,且新的线程请求分配内存时,ptmalloc2 可能会创建一个新的 Arena。
非主 Arena 的堆内存是通过
mmap系统调用从操作系统映射的匿名内存段。一个 Arena 可以管理多个
mmap映射的堆段,这些堆段通过链表连接在一起。
3. Arena 的内部结构(宏观视角)
每个 Arena 结构体(malloc_state)都包含的所有核心组件:
锁(mutex):保护 Arena 内部数据结构(Bins、Top Chunk 等)的互斥锁,防止多线程同时修改。
Bins 数组:包括
fastbinsY(Fastbins)、bin(Small/Large Bins 的数组)以及unsorted bin。Top Chunk 指针:指向当前 Arena 堆顶部的可用内存块。
Heap 信息:对于非主 Arena,会有一个链表记录其管理的所有
heap_info结构,每个heap_info描述一个通过mmap映射的堆段。统计信息:如已分配的内存总量、可回收的内存等。
4. Arena 的分配与调度策略(它是如何工作的?)
当一个线程首次调用malloc时,ptmalloc2 会按以下逻辑为其分配 Arena:
尝试获取已存在的 Arena:
系统会遍历一个全局的 Arena 链表,尝试找到一个未被锁定的 Arena。
如果找到,则将其分配给当前线程(线程与 Arena 之间会建立关联,以提高后续操作的局部性)。
创建新的 Arena(如果允许):
如果所有 Arena 都被锁定,并且当前 Arena 总数未达到上限(
narenas),ptmalloc2 会创建一个新的 Arena,并将其分配给当前线程。
阻塞等待(如果达到上限):
如果 Arena 总数已达到上限,并且所有 Arena 都在被其他线程使用,当前线程就会阻塞,等待某个 Arena 被释放(即等待其锁被释放)。这与 “锁” 的行为一致。
一个重要的细节是:线程与 Arena 的绑定关系并非永久性的。当线程释放其 Arena 的锁后,其他线程可以抢到这个 Arena。这种机制确保了 Arena 资源在繁忙线程间能得到充分利用。
5. 关键特性与注意事项
Arena 是内存增长的“边界”:每个 Arena 都有自己的 Top Chunk,其堆空间的增长是独立的。主 Arena 受限于程序 break 的单一位置,而非主 Arena 的多个堆段则可以通过
mmap在虚拟地址空间中分散分布。内存无法跨 Arena 归还:一个 Arena 无法归还另一个 Arena 的内存。如果一个线程在其 Arena 中分配了大量内存并释放,这些内存会留在该 Arena 的空闲链表中,只会被该 Arena 自身重用,不会给其他 Arena 使用。这可能导致内存闲置:一个 Arena 中缓存了大量空闲内存,而另一个 Arena 却因空间不足需要向操作系统申请新内存。
Arena 的“附加”与“脱离”:当通过
mmap为 Arena 扩展堆段时,新的堆段会附加到 Arena 的堆段链表中。当堆段顶部完全空闲时(即 Top Chunk 位于该堆段且足够大),该堆段可能被munmap脱离并释放回操作系统。对调试的影响:在多线程程序中,观察到内存分配时,需要意识到可能有多个 Arena 存在。使用
gdb的info malloc-stats或特定malloc_stats()函数打印的信息时,会看到每个 Arena 的独立统计。
总结
Arena 是 ptmalloc2 多线程能力的基石:
它通过资源隔离和锁粒度细化,显著减少了多线程内存分配时的锁竞争,提升了程序在大型多核服务器上的性能和可扩展性。
每个 Arena 都是一个功能完备的小型内存分配器,拥有自己独立的 Bin 和 Top Chunk。
它的调度策略在性能(避免等待)和内存使用率(限制创建数量)之间取得了微妙的平衡。