有关于ptmalloc的一些总结
在glibc下的ptmalloc的一点点分析。
关于chunk组织
- chunk指针指向一个chunk的开始处,而mem指针才是真正分配给用户的储存空间。
- chunk的第二个域的最低一位P表示前一个chunk是否是在使用当中(0代表前一个空闲,1代表在使用当中)。
当P为0时,此时prev_size才有效,第一个域表示的是前一个chunk的size,可以通过这个值取到前一个chunk的开始地址;当P为1时,表示前一个chunk在使用当中,prev_size无效。
ptmalloc分配的第一个chunk总是将P设为1,以防止程序引用到不存在的区域。 - chunk第二个域倒数第二位M(0表示是从mmap映射区域分配的,1表示heap区域分配)
- chunk第二个域倒数第三位A(1表示主分配区,0非主分配区)
- 当chunk空闲时,user_data区域储存了4个指针。
指针fd指向后一个空闲的chunk;
指针bk指向前一个空闲的chunk;
fd_nextsize以及bk_nextsize两个指针用于加快在large bin中查找最近匹配的空闲chunk - chunk利用标志来管理使用和空闲的内存,减少了内存归还所带来的系统消耗,并且在一个未使用的chunk中,它的prev_size部分是可以被紧挨的上一个使用的chunk空间复用的。
关于bins
用户free掉的内存并不是直接就归还给系统,ptmalloc会统一管理heap和mmap映射区域中的空闲chunk,当用户有分配请求时候,ptmalloc会首先在空闲的chunk中挑选一块符合需求的交给用户,这样避免了频繁的系统调用,降低了内存分配的开销。
- ptmalloc将相似大小的chunk用双向链表链接起来,这样的一个链表就被叫做bin,ptmalloc一共维护了128个bin,并用一个数组来储存这些bin
- 数组第一个是unsorted bin,ptmalloc在合并空闲的chunk时会放入,如果用户释放的chunk大于max_fast或者fast bins中空闲的chunk合并之后都会放入unsorted bin中。
ptmalloc在fast bins中没有找到合适的chunk就会进入unsorted bins中查找,如果还是没有找到,就会把unsorted bin汇总的chunk加入到bins当中,这样看来,unsorted bin类似于bins中的一个缓冲区。 - 数组的2到64个bin被称为small bins,同一个small bin中的chunk大小相同,两个相邻的small bin相差8字节;
- 后面64个bin称为large bins,这里面的每一个bin都是一个范围内的chunk并且是按大小序排列好的;
- fast bins
- 引入它是因为在分配时可能会经常申请和释放一些很小的空间,分配器合并之后又需要分割,这样太过低效,故而在不大于max_fast(默认值是64B)的chunk释放后会先被放到fast_bins中,不去改变他们的P标志位,所以他们无法合并,在小于等于max_fast时首先在fast_bins中查找相应的空闲块。
- Top chunk
这是一开始所划分出来的一个大空闲内存,如果bins之后还没有满足的chunk,那么通过Top chunk来划分一个新的chunk给用户 - mmaped chunk
当top chunk都不能满足时,通过mmap将页映射到进程空间中,这样的chunk被free时候直接解除映射归还给系统。 - last remainder
当所请求的chunk是一个小空间的,但是在small bins中又没有合适的chunk,那么从last remainder chunk中分裂出两个chunk,一个给用户,一个变成新的last remainder chunk。
sbrk和mmap
.bss段之上的分配给用户程序的空间叫做堆,start_brk指向堆的开始,brk指向堆的顶部,可以通过brk()和sbrk()来增加标识堆顶的brk的值。
在使用malloc之前brk等于start_brk,请求分配时若是请求空间小于mmap的分配阈值(mmap threshold,默认是128k),主分配区会调用sbrk()增加一块大小为4k的空间作为heap。非主分配区则会调用mmap映射一块HEAP_MAX_SIZE(32位系统下默认1M,64位系统下默认是64M)大小的空间作为sub-heap。
ptmalloc分配概述
- 获取分配区的锁,为了防止多个线程同时访问同一个分配区。
- 将用户请求的大小转换位实际需要分配的chunk空间的大小。
- 判断所需分配的chunk大小是否小于max_fast,如果大于则跳转第五步。
- 首先城市在fast bins中查找一个chunk给用户,如果存在则分配结束。
- 判断所需大小是否处在small bins中,如果chunk大小处在small bins中,则根据所需chunk大小,找到具体所在的某个索small bin,从bin的尾部摘取一个满足大小的chunk返回给用户。否则到第六步。
- 到了这一步说明分配的是一个大块内存或者small bins中没有合适的chunk。那么首先会遍历fast bins中的chunk与相邻chunk合并链接到unsorted bin中,然后遍历unsorted bin中的chunk,如果只有一个上次分配已经使用的chunk,并且这个chunk满足small bins的要求且这个chunk符合用户所要求的大小,那么直接对这个chunk进行分割返回给用户,否则将它放入small bins或者large bins中。
- 排除了fast bins和unsorted bin中的chunk,到了这一步从large bins中找到一个合适的chunk,从中划分一块所需大小的chunk,并将剩余部分链接回bins中,若分配成功则结束,否则下一步。
- 如果bins中都没有合适的chunk,那么则对top chunk进行分割来分配给用户,成功则结束,否则下一步。
- 到了这一步证明top chunk不能满足需求,那么对于主分配区就调用sbrk()来增加top chunk的大小;对于非主分配区则会调用mmap来分配一个新的sub-heap增加top chunk的大小或者直接mmap()直接映射分配。
关于内存的回收
- free()函数也会先获取分配区的锁。
- 判空,如果是空直接return。
- 判断所需释放的chunk是不是mmaped chunk,若是是,直接调用munmap()释放mmaped chunk,解除内存映射。否则下一步。
- 判断chunk的大小和所处的位置,如果chunk_size<=max_fast,并且chunk不在堆顶,也就不与top chunk相邻,则转到下一步,否则转到第六步。
- 将chunk放到fast bins中,将chunk放入到fast bins中,并不修改当前的chunk的使用标志P,之后释放结束从free()中返回。
- 判断前一个chunk是否在使用当中,如果前一个块也是空闲块那么进行合并。
- 判断当前释放的下一个块是不是top chunk,如果是则转到第九步,否则下一步。
- 判断下一个chunk是否处在使用中,如果下一个chunk也是空闲的则合并后放入unsorted bin中。
- 如果执行到这一步,说明释放了一个和top chunk相邻的chunk。
- 判断合并后的chunk大小是否大于FASTBIN_CONSOLDATION_THRESHOLD(默认64KB),如果是则触发进行fast bins的合并操作。
- 判断top chunk是否大于mmap的收缩阈值(默认128K),如果是的话,对于主分配区,则会试图归还top chunk中的一部分给操作系统。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。