top Chunk,堆中第一个堆块,它处于整个堆内存的顶端,程序以后分配到的内存都要放在它的后面。
可以把 Top Chunk 想象成 Arena 里的一大块“空白画布”或“备用金库”。当所有空闲链表(Bin)都无法满足内存请求时,分配器就会从这里“切”一块下来。
1. 它是什么?在哪?
-
定义:Top Chunk 是每个 Arena(分配区)堆顶部的一个特殊的、非常大的空闲块。它并不在任何空闲链表(Bin)中,而是由 Arena 的管理结构直接指向。
-
物理位置:它位于当前堆内存的最高地址处,紧挨着所有已分配或空闲的 chunk 之上。可以把它看作堆的“边界”或“天花板”。
-
初始状态:当 Arena 刚刚创建或从系统申请到新内存时,整个新获得的内存区域就是 Top Chunk。
2. 核心工作流程
Top Chunk 的行为逻辑非常清晰,主要涉及“切割”和“扩容”两个动作。
A. 分配内存:最后的“切割者”
当 malloc 请求在 tcache、fastbin、unsorted bin 和 small/large bins 中都找不到合适的内存块时,程序就会找到 Top Chunk:
-
检查空间:系统会检查 Top Chunk 的当前大小是否大于等于请求的大小。
-
执行切割:
-
如果空间足够,ptmalloc2 会直接从 Top Chunk 的底部切出一块请求大小的内存,返回给用户。
-
Top Chunk 的指针会向上移动,其剩余大小会相应减少。
-
-
直接分配:这是一个非常快速的操作,因为没有复杂的链表查找或合并逻辑。
B. 扩容(当 Top Chunk 空间不足时)
如果 Top Chunk 剩余的空间小于请求的大小,会发生以下情况:
-
尝试合并:ptmalloc2 会检查紧邻 Top Chunk 下方的那个 chunk。如果它是空闲的(比如在 fastbin 或 unsorted bin 中),分配器会尝试将其与 Top Chunk 合并,以增大 Top Chunk 的空间。
-
向系统申请:如果合并后仍然不够,ptmalloc2 就会通过系统调用向操作系统申请更多的内存。
-
主 Arena(main arena):使用
brk/sbrk系统调用,扩展堆的program break,从而增大 Top Chunk。 -
非主 Arena(thread arena):使用
mmap系统调用,从操作系统映射一块新的连续内存区域,挂接到当前堆上,作为新的 Top Chunk。
-
-
切割返回:在成功扩容后,从新的、足够大的 Top Chunk 中切割出所需的部分返回给用户。
3. 关键特性与注意事项
-
释放内存的“终点”:当
free释放一个位于 Top Chunk 紧邻下方的空闲块,并且这个块与 Top Chunk 物理相邻时,它们会被合并,使 Top Chunk 变大。这是 ptmalloc2 将内存归还给操作系统的唯一途径。只有 Top Chunk 顶部的空闲内存,才有可能通过brk或munmap真正地释放回内核,从而降低进程的物理内存占用(RSS)。 -
内存碎片的“屏障”:如果 Top Chunk 下方存在一个很小的、尚未释放的已分配内存块,即使 Top Chunk 本身很大,它也无法越过这个小块与下方更大的空闲块合并。这就形成了内存碎片,导致虽然总空闲内存很多,但 Top Chunk 无法增长,某些大内存申请可能会因为找不到连续空间而失败,或被迫使用
mmap单独分配。 -
相关阈值:ptmalloc2 有一个
trim_threshold参数。当 Top Chunk 的空闲空间超过这个阈值时(默认为 128KB),free操作可能会触发一个“收缩(trim)”动作,尝试将 Top Chunk 顶部多余的内存归还给操作系统。 -
多线程下的角色:在非主 Arena 中,每个线程的 Arena 都有自己的 Top Chunk。当 Top Chunk 耗尽并通过
mmap扩容时,会映射新的堆段。这些堆段通过链表管理,其中当前活跃的、用于分配的最新的堆段,其顶部就是 Top Chunk。
glibc 中对于 top chunk 的描述如下
/*
Top
The top-most available chunk (i.e., the one bordering the end of
available memory) is treated specially. It is never included in
any bin, is used only if no other chunk is available, and is
released back to the system if it is very large (see
M_TRIM_THRESHOLD). Because top initially
points to its own bin with initial zero size, thus forcing
extension on the first malloc request, we avoid having any special
code in malloc to check whether it even exists yet. But we still
need to do so when getting memory from system, so we make
initial_top treat the bin as a legal but unusable chunk during the
interval between initialization and the first call to
sysmalloc. (This is somewhat delicate, since it relies on
the 2 preceding words to be zero during this interval as well.)
*/
/* Conveniently, the unsorted bin can be used as dummy top on first call */
#define initial_top(M) (unsorted_chunks(M))
程序第一次进行 malloc 的时候,heap 会被分为两块,一块给用户,剩下的那块就是 top chunk。其实,所谓的 top chunk 就是处于当前堆的物理地址最高的 chunk。这个 chunk 不属于任何一个 bin,它的作用在于当所有的 bin 都无法满足用户请求的大小时,如果其大小不小于指定的大小,就进行分配,并将剩下的部分作为新的 top chunk。否则,就对 heap 进行扩展后再进行分配。在 main arena 中通过 sbrk 扩展 heap,而在 thread arena 中通过 mmap 分配新的 heap。
需要注意的是,top chunk 的 prev_inuse 比特位始终为 1,否则其前面的 chunk 就会被合并到 top chunk 中。
初始情况下,可以将 unsorted chunk 作为 top chunk。
总结
Top Chunk 是 ptmalloc2 的内存之源:
-
它是所有分配请求在缓存(Bins)失败后的最终保障。
-
它是程序将内存归还给操作系统的唯一出口。
-
它的扩张和收缩,直接反映了程序从操作系统申请和释放内存的宏观行为。
理解 Top Chunk 及其与 brk/mmap 的交互,是分析程序虚拟内存布局和实际物理内存占用(RSS)的关键。进程的 RSS 居高不下,很多时候就是因为有一个已分配的小块内存“顶”在 Top Chunk 的下方,导致其无法收缩,无法释放其上的大块空闲内存。

2122

被折叠的 条评论
为什么被折叠?



