堆的概述
在内存管理领域,glic通过brk和mmap两大系统调用,构建了一套高效的堆内存管理机制。层级化架构,包含分配区(Arena) 堆(Heap) 内存块(Chunk). 主Arena依赖brk系统调用实现内存分配,而子Arena通过mmap完成内存获取.多线程程序运行时,每个线程通常会有自己的Arena,主线程与子线程的堆空间各自独立管理,互不干扰.
fast bins,small bins large bins,unsorted bin 在具体实现malloc和free函数时.glibc遵循“先小后大 最佳适配”原则.
brk()和sbrk()
include<unistd.h>
int brk(void* end_data_segment);
void *sbrk(intptr_t increment);
brk()的参数是一个指针,用来设置program_break指向的位置.sbrk()函数的参数increment(可以是负值)用于和program_break相加调整program_break的值,成功执行后brk()函数会返回0,sbrk()函数会返回上一次program_break值.也就是说,设置increment为0,可以获得当前program_break的值.
mmap()和unmmap()
include<sys/mman.h>
void *mmap(void *addr,size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);
当用户申请内存过大(在大多数系统中,阈值通常为128KB)时,ptmalloc2会选择通过mmap()函数创建匿名映射段供用户使用,并通过unmmap()函数回收.
Glibc堆内存管理核心机制
用自己的话来概括一下吧,上文已经提到 有Arena Heap Chunk,这三个主要的元素在堆内存管理上发挥作用。
首先,Arena分为主分配区和子分配区,其中主分配区的Heap主要有brk系统调用,即修改指向位置和加减数字来决定大小。主Arena只有一个!
子分配区可以有多个,主要通过mmap系统调用创建,子Arena之间通过链表连接,物理空间不需要连续。
Chunk
在malloc调用后,chunk会依次在fast bins,small bins和large bins, unsorted bins中寻找合适的内存块,也就是说如果没找到,会被直接放在unsorted bins中
glib中堆内存分配的基本思路就是,首先找到本线程的Arena,然后优先在Arena对应的回收箱中寻找合适大小的内存,在内存箱中所有内存块均小于所需求的大小,那么就会去top chunk分割,但是如果top chunk的大小也不足够,此时不一定要拓展top,检查所需的内存是否大于128k,若大于,则直接使用系统调用mmap分配内存,如果小于,就进行top chunk的拓展,即堆的拓展,拓展完成后,从top chunk中分配内存,剩余部分成为新的top chunk。