Ptmalloc总结

有关于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分配概述

  1. 获取分配区的锁,为了防止多个线程同时访问同一个分配区。
  2. 将用户请求的大小转换位实际需要分配的chunk空间的大小。
  3. 判断所需分配的chunk大小是否小于max_fast,如果大于则跳转第五步。
  4. 首先城市在fast bins中查找一个chunk给用户,如果存在则分配结束。
  5. 判断所需大小是否处在small bins中,如果chunk大小处在small bins中,则根据所需chunk大小,找到具体所在的某个索small bin,从bin的尾部摘取一个满足大小的chunk返回给用户。否则到第六步。
  6. 到了这一步说明分配的是一个大块内存或者small bins中没有合适的chunk。那么首先会遍历fast bins中的chunk与相邻chunk合并链接到unsorted bin中,然后遍历unsorted bin中的chunk,如果只有一个上次分配已经使用的chunk,并且这个chunk满足small bins的要求且这个chunk符合用户所要求的大小,那么直接对这个chunk进行分割返回给用户,否则将它放入small bins或者large bins中。
  7. 排除了fast bins和unsorted bin中的chunk,到了这一步从large bins中找到一个合适的chunk,从中划分一块所需大小的chunk,并将剩余部分链接回bins中,若分配成功则结束,否则下一步。
  8. 如果bins中都没有合适的chunk,那么则对top chunk进行分割来分配给用户,成功则结束,否则下一步。
  9. 到了这一步证明top chunk不能满足需求,那么对于主分配区就调用sbrk()来增加top chunk的大小;对于非主分配区则会调用mmap来分配一个新的sub-heap增加top chunk的大小或者直接mmap()直接映射分配。

关于内存的回收

  1. free()函数也会先获取分配区的锁。
  2. 判空,如果是空直接return。
  3. 判断所需释放的chunk是不是mmaped chunk,若是是,直接调用munmap()释放mmaped chunk,解除内存映射。否则下一步。
  4. 判断chunk的大小和所处的位置,如果chunk_size<=max_fast,并且chunk不在堆顶,也就不与top chunk相邻,则转到下一步,否则转到第六步。
  5. 将chunk放到fast bins中,将chunk放入到fast bins中,并不修改当前的chunk的使用标志P,之后释放结束从free()中返回。
  6. 判断前一个chunk是否在使用当中,如果前一个块也是空闲块那么进行合并。
  7. 判断当前释放的下一个块是不是top chunk,如果是则转到第九步,否则下一步。
  8. 判断下一个chunk是否处在使用中,如果下一个chunk也是空闲的则合并后放入unsorted bin中。
  9. 如果执行到这一步,说明释放了一个和top chunk相邻的chunk。
  10. 判断合并后的chunk大小是否大于FASTBIN_CONSOLDATION_THRESHOLD(默认64KB),如果是则触发进行fast bins的合并操作。
  11. 判断top chunk是否大于mmap的收缩阈值(默认128K),如果是的话,对于主分配区,则会试图归还top chunk中的一部分给操作系统。

你可能感兴趣的:(glibc,内存管理,malloc)