malloc内存分配原理
malloc基本的实现原理就是维护一个内存空闲链表,当申请内存空间时,搜索内存空闲链表,找到适配的空闲内存空间,然后将空间分割成两个内存块,一个变成分配块,一个变成新的空闲块。如果没有搜索到,那么就会用sbrk()才推进brk指针来申请内存空间。
搜索空闲块最常见的算法有:首次适配,下一次适配,最佳适配。
首次适配:第一次找到足够大的内存块就分配,这种方法会产生很多的内存碎片。
下一次适配:也就是说等第二次找到足够大的内存块就分配,这样会产生比较少的内存碎片。
最佳适配:对堆进行彻底的搜索,从头开始,遍历所有块,使用数据区大小大于size且差值最小的块作为此次分配的块。
合并空闲块
在释放内存块后,如果不进行合并,那么相邻的空闲内存块还是相当于两个内存块,会形成一种假碎片。所以当释放内存后,我们需要将两个相邻的内存块进行合并。
显式空闲链表
还有一种实现方式则是采用显示空闲链表,这个是真正的链表形式。在之前的有效载荷中加入了之前前驱和后驱的指针,也可以称为双向链表。维护空闲链表的的方式第一种是用后进先出(LIFO),将新释放的块放置在链表的开始处。另一种方法是按照地址的顺序来维护。
在PCB中的mm_struct结构中保存了堆区的开始和结束位置,在malloc第一次调用的过程中,操作系统会分配给程序4k的空间,C++会将这4k的空间以链表的方式管理起来,以最小粒度为单位(64B),分配给用户,用户只能使用自己申请的大小,在mm_struct结构中用brk指针记录当前链表的使用末尾,再次使用时,尾指针往后挪,释放时,并不是释放之后就将brk指针紧缩,而是brk之下有128k空闲区间时才进行紧缩,在这期间如果有内存分配,就把brk之下的空闲空间分配给他,在一页分配完毕之后,在申请一页,一次申请的空间大于128k时,会使用mmap函数的匿名映射功能,在brk之上,栈区之下的虚拟地址中,映射一块空间,Linux一次申请最大2.9G,累计申请2.9G,Windows一次申请最大不到2G,累计申请也不到2G