深入理解glibc malloc:malloc 与 free() 原理图解

本文分为三个等级自顶向下地分析了glibc中内存分配与回收的过程 。本文不过度关注细节,因此只是分别从arena层次、bin层次、chunk层次进行图解,而不涉及有关指针的具体操作 。
前言在展开本文之前,先解释一下本文中会提到的三个重要概念:arena,bin,chunk 。三者在逻辑上的蕴含关系一般如下图所示(图中的chunk严格来说应该是Free Chunk) 。

深入理解glibc malloc:malloc 与 free() 原理图解

文章插图
 
三者概念的解释如下:
arena:通过sbrk或mmap系统调用为线程分配的堆区,按线程的类型可以分为2类:
  • main arena:主线程建立的arena;
  • thread arena:子线程建立的arena;
chunk:逻辑上划分的一小块内存,根据作用不同分为4类:
  • Allocated chunk:即分配给用户且未释放的内存块;
  • Free chunk:即用户已经释放的内存块;
  • Top chunk
  • Last Remainder chunk
bin:一个用以保存Free chunk链表的表头信息的指针数组,按所悬挂链表的类型可以分为4类:
  • Fast bin
  • Unsorted bin
  • Small bin
  • Large bin
在这里读者仅需明白arena的等级大于bin的等级大于(free)chunk的等级即可,即A>B>C 。
tips:
【深入理解glibc malloc:malloc 与 free() 原理图解】实际内存中,main arena和thread arena的图示如下(单堆段) 。
深入理解glibc malloc:malloc 与 free() 原理图解

文章插图
 
其中malloc_state的数据结构描述在源代码中发现该数据结构中保存着fastbinsY、top、last_remainder、bins这四个分别表示Fast bin、Top chunk、Last Remainder chunk、bins(Unsorted bin、 Small bin、Large bin)的数据 。
Arena级分析此处从Arena的层次分析内存分配与回收的过程 。
main arena中的内存申请main arena中的内存申请的流程如下图所示:
深入理解glibc malloc:malloc 与 free() 原理图解

文章插图
 
第一次申请
  • 根据申请内存空间大小是否达到mmap这一系统调用的分配阈值,决定是使用sbrk系统调用 还是mmap系统调用申请堆区 。一般分配的空间比申请的要大,这样可以减少后续申请中向操作系统申请内存的次数 。
  • 举例而言,用户申请1000字节的内存,实际会通过sbrk系统调用产生132KB的连续堆内存区域 。
  • 然后将用户申请大小的内存返回 。(本例中将返回1000字节的内存 。)
后续申请
  • 根据arena中剩余空间的大小决定是继续分配还是扩容,其中包含扩容部分的为top chunk 。
  • 然后将用户申请大小的内存返回 。
tips: top chunk不属于任何bin!只有free chunk依附于bin! 分配阈值具有默认值,但会动态调整; 扩容具体过程见库函数sYSMALLOc。
thread arena中的申请thread arena中的内存申请的流程如下图所示:
深入理解glibc malloc:malloc 与 free() 原理图解

文章插图
 
其流程类似于main arena的,区别在于thread arena的堆内存是使用mmap系统调用产生的,而非同主线程一样可能会使用sbrk系统调用 。
tips:Arena的数量与线程之间并不一定是一一映射的关系 。如,在32位系统中有着“ Number of arena = 2 * number of cores + 1”的限制 。
内存回收
深入理解glibc malloc:malloc 与 free() 原理图解

文章插图
 
线程释放的内存不会直接返还给操作系统,而是返还给’glibc malloc’ 。
bin级分析此处从bin的层次分析内存分配与回收的过程 。考虑到内存回收的过程比内存分配的过程要复杂,因此这里先分析内存回收的过程,再分析内存分配的过程 。
内存回收内存回收的流程如下图所示:
深入理解glibc malloc:malloc 与 free() 原理图解

文章插图
 
bin可以分为4类:Fast bin、Unsorted bin、Small bin和 Large bin 。保存这些bin的数据结构为fastbinsY以及bins:
fastbinsY:用以保存fast bins 。(可索引大小16~64B的内存块)
bins:用以保存unsorted、small以及large bins,共计可容纳126个: