Malloc技术原理解析以及在转转搜索业务上的实践( 四 )


  1. 获取分配区(arena)的锁 , 以确保多线程环境下的内存分配操作不会发生冲突;
  2. 计算出需要分配的chunk的实际大小;
  3. 首先检查chunk的大小是否小于max_fast(默认64字节) , 如果是 , 则尝试从fast bins中获取适合的chunk , 如果找到则分配结束 。否则 , 继续下一步;
  4. 如果chunk的大小小于512字节 , 尝试从small bins中获取合适的chunk , 如果找到则分配结束 。否则 , 继续下一步;
  5. 如果以上步骤都未成功 , 会首先遍历fast bins中的chunk , 将相邻的chunk合并 , 并链接到unsorted bin中 , 然后遍历unsorted bins 。如果unsorted bins中只有一个chunk且大小大于待分配的chunk , 则将其切分 , 并将剩余的部分继续放入unsorted bins;如果unsorted bins中有大小适合的chunk , 则分配结束 。否则 , 继续下一步;
  6. 如果以上步骤都未成功 , 在large bins中查找合适的chunk , 进行切割 , 并将剩余部分放回unsorted bin 。如果fast bins和bins都没有找到合适的chunk , 那么就需要操作top chunk来进行分配了 。如果top chunk的大小比用户所请求的大小还大 , 则将top chunk切分为两部分:User chunk(用户请求大小)和Remainder chunk(剩余大小) , 并将剩余块作为新的top chunk 。如果top chunk的大小小于用户所请求的大小 , 根据分配区的类型(主分配区或非主分配区) , 通过sbrk(主分配区)或mmap(非主分配区)系统调用来扩展top chunk的大小;
  7. 如果以上步骤都未成功 , 根据需要的chunk大小 , 选择调用sbrk(主分配区)或mmap(非主分配区)系统调用来分配内存块;
  8. 使用mmap系统调用为程序的内存空间映射一块大小为chunk_size、对齐到4KB的内存空间 , 并将内存指针返回给用户;
  9. 判断是否为第一次调用malloc , 如果是主分配区 , 则需要进行初始化工作 , 分配一块初始大小为(chunk_size + 128KB)、对齐到4KB的空间作为初始的heap 。如果已经初始化过了 , 主分配区则调用sbrk(主分配区)来增加heap的大小 , 非主分配区则在top chunk中切割出一个chunk以满足分配需求 。
3.3 回收过程内存回收流程概述如下:
  1. 首先 , 获取分配区的锁 , 以确保线程安全性;
  2. 如果要释放的是空指针 , 则无需执行任何操作 , 直接返回;
  3. 检查当前的内存块(chunk)是否是通过mmap映射的内存区域 。如果是的话 , 直接通过munmap()函数将该chunk释放 。我们可以通过已使用chunk的数据结构中的"M"标志来判断是否是mmap映射的内存;
  4. 判断chunk是否与顶部内存块(top chunk)相邻 。如果相邻 , 将它们合并在一起(相邻意味着与分配区中的空闲chunk相邻) 。然后 , 继续下一步骤;
  5. 如果chunk的大小大于max_fast(64字节) , 将其放入未排序的bin中 , 并检查是否需要合并 。如果需要合并且与top chunk相邻 , 进入下一步骤;
  6. 如果内存块的大小小于max_fast(64字节) , 直接放入快速bin中 , 而不改变chunk的状态 。然后 , 检查是否需要合并 , 如果需要合并 , 继续下一步骤;
  7. 在快速bin中 , 如果当前chunk的下一个chunk也是空闲的 , 将它们合并 , 并将合并后的chunk放回未排序的bin中 。如果合并后的chunk大小大于64字节 , 将触发快速bin的合并操作 , 将与相邻的空闲chunk合并后 , 放回未排序的bin中 。如果合并后的chunk与top chunk相邻 , 将它们合并到top chunk中;
  8. 最后 , 检查top chunk的大小是否大于mmap的收缩阈值(默认为128KB) 。如果是 , 尝试将部分top chunk归还给操作系统 , 然后结束内存释放操作 。


    推荐阅读