Glibc一般采用ptmalloc2这个版本去管理内存。进程的内存区域分配如下:
| Kernel Space |
| Undefined Region |
| Stack |
| \|/ |
| Memory Mapping Region |
| /|\ |
| Head |
| BSS Segment |
| Data Segment |
| Text Segment |
一般对heap的操作提供了两种接口,一是brk()的系统调用,设置了heap的上边界。
对于mmap映射区域提供了mmap()和munmap两种系统调用,因为系统调用的代价很高,用mmap的区域很容易被释放,所以一般小区域的内存用brk申请,大区域的内存用mmap申请;
PtMalloc支持多线程,有一个主分配区域(main arena),有多个非主分配区。某个线程调用malloc时候。会先查看线程私有变量中是否已经存在一个分配区,如果存在则尝试加锁,如果加锁失败则会遍历一遍arena链表试图获取一个没有加锁的arana,如果依然获取不到则创建一个新的分配区(arena)。
PtMalloc的内存管理:
用户请求分配的内存再ptmalloc中使用chunk(组,大块,厚片)表示。每个chunk至少需要8个字节的额外开销。用户free掉的内存不会立马还给操作系统。Ptmalloc会统一管理heap和mmap区域的空闲chunk。避免频繁的系统调用。
Ptmalloc将相似大小的chunk用双向链表连在一起。这样的链表称为一个bin,Ptmalloc一个维护的128个bin,并用数组来维护这些bin。(就像一个hashmap一样,每个数组节点下面都有一个链表,这个链表就是bin)
数组中的第一个称为unsorted bin,数组中从2开始编号的前64个bin称为small bin,同一个small bin中的chunk大小相同。Small bin后面的bin称为large bin。
当释放一个chunk的时候,ptmalloc会检查它前后的chunk有咩有空闲的chunk,有空闲的chunk就会与其合并并且移动到一个bin也就是unsorted bin中,同时也会把
一些小的chunk(小于64b)放到一个fast bins中。
如果fast bins和bins都不能满足需求后,ptmalloc会设法在一个top chunk的空间分配内存,对于非主arena会先通过mmap申请一块大的内存给top chunk。
如果topchunk本身还是不够大, 分配程序会重新mmap分配一块内存chunk,并且将top chunk迁移到新的chunk上,如果free()的chunk刚好与top chunk相邻,那么这两个chunk就会合并成新的topchunk,如果topchunk的大小大于某个阈值,才会还给操作系统。主分配区类似,不过sbrk()的分配和调整topchunk的大小,只有在heap的顶部连续内存空闲超过阈值的时候才会回收内存。
需要的chunk足够大,而且fast bins和bins都不能满足要求,甚至topchunk本身也不能满足分配需求时候,ptmalloc会使用mmao来直接使用内存映射关联到进程空间。