本文分为三个等级自顶向下地分析了glibc中内存分配与回收的过程 。本文不过度关注细节,因此只是分别从arena层次、bin层次、chunk层次进行图解,而不涉及有关指针的具体操作 。
前言在展开本文之前,先解释一下本文中会提到的三个重要概念:arena,bin,chunk 。三者在逻辑上的蕴含关系一般如下图所示(图中的chunk严格来说应该是Free Chunk) 。
文章插图
三者概念的解释如下:
arena:通过sbrk或mmap系统调用为线程分配的堆区,按线程的类型可以分为2类:
- main arena:主线程建立的arena;
- thread arena:子线程建立的arena;
- Allocated chunk:即分配给用户且未释放的内存块;
- Free chunk:即用户已经释放的内存块;
- Top chunk
- Last Remainder chunk
- Fast bin
- Unsorted bin
- Small bin
- Large bin
tips:
【深入理解glibc malloc:malloc 与 free() 原理图解】实际内存中,main arena和thread arena的图示如下(单堆段) 。
文章插图
其中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中的内存申请的流程如下图所示:
文章插图
第一次申请
- 根据申请内存空间大小是否达到mmap这一系统调用的分配阈值,决定是使用sbrk系统调用 还是mmap系统调用申请堆区 。一般分配的空间比申请的要大,这样可以减少后续申请中向操作系统申请内存的次数 。
- 举例而言,用户申请1000字节的内存,实际会通过sbrk系统调用产生132KB的连续堆内存区域 。
- 然后将用户申请大小的内存返回 。(本例中将返回1000字节的内存 。)
- 根据arena中剩余空间的大小决定是继续分配还是扩容,其中包含扩容部分的为top chunk 。
- 然后将用户申请大小的内存返回 。
tips: top chunk不属于任何bin!只有free chunk依附于bin! 分配阈值具有默认值,但会动态调整; 扩容具体过程见库函数sYSMALLOc。thread arena中的申请thread arena中的内存申请的流程如下图所示:
文章插图
其流程类似于main arena的,区别在于thread arena的堆内存是使用mmap系统调用产生的,而非同主线程一样可能会使用sbrk系统调用 。
tips:Arena的数量与线程之间并不一定是一一映射的关系 。如,在32位系统中有着“ Number of arena = 2 * number of cores + 1”的限制 。内存回收
文章插图
线程释放的内存不会直接返还给操作系统,而是返还给’glibc malloc’ 。
bin级分析此处从bin的层次分析内存分配与回收的过程 。考虑到内存回收的过程比内存分配的过程要复杂,因此这里先分析内存回收的过程,再分析内存分配的过程 。
内存回收内存回收的流程如下图所示:
文章插图
bin可以分为4类:Fast bin、Unsorted bin、Small bin和 Large bin 。保存这些bin的数据结构为fastbinsY以及bins:
fastbinsY:用以保存fast bins 。(可索引大小16~64B的内存块)
bins:用以保存unsorted、small以及large bins,共计可容纳126个:
- Bin 1 – unsorted bin
- Bin 2 to Bin 63 – small bin(可索引大小<512B的内存块)
推荐阅读
- 图解希尔排序,超详细非常好理解
- 深入理解 C 语言的 hello world
- JAVA并发之ReentrantLock原理解析
- 如何理解c/c++和php语言的区别
- 如何理解中台?
- C/C++|图文深入理解函数调用的5种约定
- Linux 升级gcc g++ gdb glibc教程
- TCP和UDP的区别,深入理解TCP三次握手和四次挥手的全过程
- 微软在Edge浏览器中更深入整合Office功能
- linux内核--自旋锁的理解