ptmalloc一些细节流程
- chunk 容器
- Bins
- ptmalloc统一管理heap和mmap区的chunk,避免了频繁的系统调用,一共维护了128个bin,使用数组来存储,所有的bin都是双向链表。
2.Fast Bins
-
- 对于不大于max_fast的chunk加入fast bins,标志位P不改变(为了不使其合并),在认为内存碎片太多时会将其合并加入unsorted bin。
3.Unsorted Bin
-
- 对于回收的chunk大于max_fast或合并完的 fast bin 会加在这里,在fast bin中没有找到合适的chunk后,会在这里找,如果没有将其加入到合适的bin中,相当于其他bin的高速缓存。
4.Top chunk
-
- 在sub_heap的最高处不属于任何chunk,总是在small bin 和large bin 后考虑,与收缩条件息息相关。
5.mmap chunk
-
- 申请的内存足够大时,直接使用mmap向系统申请资源,同时释放时也是直接解除映射。
6.Last remainder
-
- 一种特殊的chunk,不属于任何bin,当需要分配一个small chunk时,没有相应的chunk,last remainder chunk会分裂成两个,一个返回给用户,另一个变成新的last remainder chunk。
- 核心结构
- 对于主分配区,使用了静态全局的结构体定义(malloc_par),保证只有一份。
- 对于非主分配区,由多个sub_heap组成。第一个sub_heap中,最顶端是heap_info,接下来是malloc_state的实例(保存了分配去相应的信息),接下来是chunk。heap_info中利用单链表将sub_heap连接起来,后续的sub_heap由heap_info和chunk构成。
- 初始化
- 分配区的初始化:首先遍历所有的bins,修改指针指向,设置了全局变量global_max_fast,在主分配区初始化时赋值,最后初始化top_chunk。
- ptmalloc初始化:
- 调用malloc前定义了hooks函数,对ptmalloc进行初始化,对于多线程的ptmalloc,同时需要调用pthread_initilaize初始化pthread。
- 定义了判断是否初始化过的全局变量_malloc_initalized(值为1时说明初始化完成)。
- 初始化全局锁list_lock用于同步分配区的单向循环链表,创建线程私有实例arena_key只想主分配区,以后每次分配内存后调整arena_key使不总在一个分配区分配资源。
- 初始化fork子进程时调用的回调函数:在本线程fork子进程时,调用ptmalloc_lock_all()获得所有分配区的锁,当子进程创建完毕时,父进程调用ptmalloc_unlock_all()重新unlock每个分配区的锁,子进程调用ptmallic_unlock_all2()重新初始化每个分配区的锁。对于从父进程继承的锁不安全会造成内存泄漏,所以需要重新初始化。另外对于递归fork子进程时,使用了atfork_recursive_cntr引用计数,方便父进程正确解锁。
- 获取分配区:(PER_THREAD开启)首先尝试从分配区的free_list中获得一个分配区(由父进程继承而来),如果没有则尝试重用分配区,如果仍然没有则创建新的分配区。
- 当_int_malloc()函数尝试从fast bins,last remainder chunk,small bins,large bins和top chunk都失败都就会使用sysMalloc()函数直接向系统申请内存用于分配所需的chunk。