原文:http://www.cnblogs.com/huhuuu/p/3456662.html
前段时间一直想看malloc的原理,在搜了好几篇malloc源码后遂放弃,晦涩难懂。
后来室友买了本深入理解计算机系统的书,原来上面有讲malloc的原理,遂看了,先明白理论,在看代码就理解比较快了= =
1.问题的引入:
为什么要使用malloc,主要是因为在代码中,为了节约内存,很多数据都是动态生成的,所以会用malloc,对应于C++中的new,底层还是调用malloc.
2.碎片的问题:
会有内部碎片与外部碎片的问题,内部碎片难以消除(因为字对齐之类的问题),而外部碎片是可以消除的(如果不消除的话,外部的内存块越来越小,虽然数量多了,但是利用率会急剧下降!)
3.需要解决的问题:
4.隐式的空闲链表:
一个简单的堆块,这里只有头部,下面为了优化,还会使用尾部。
隐式的空闲链表:
这样就把用链表的方法把堆空间给联系起来了。特点明显,实现简单,但分配时查询空堆块是线性时间的。
5.放置以分配的块:
1)、首次适配:从链表开始寻找适合的空堆块,直到找到为止
2)、下一次适配:从上一次的分配点开始找,直到找到适合的空堆块为止
3)、最佳适配:查询整个链表,找到最合适(浪费最小)的空堆块
6.分割空闲块:
当前的分配的空堆块比所需要的空间大很多的时候,如果不分割,就会照成内部碎片过大,利用率下降。
当需要3个字节的空间时,首次适配找到了满足条件的第二个堆块,但堆块过大,所以分割成两个16字节的堆块。
7.获取额外的堆存储器:
当链表中不能满足申请要求的堆块空间的时候,1)通过合并相邻的堆块空间,形成单个尽量大的堆块空间 2)实在没有其他办法了,分配器通过sbrk函数向内核申请格外的堆空间,分配器将堆空间插入到链表中,然后提供给申请空间的块。
8.合并
如果,在如上的情况下,合并两个块,由于当前块不知道上一个块的情况,它要通过遍历整个链表才能知道上一个堆块的空间是否为空,这样将花费线性的时间来合并与当前相邻的空闲堆块。有个类似双向链表的方法(不过不是真正的双向链表,只是可以加一个信息量,直接就可以知道上一个堆块的情况)
上一个堆块的情况就容易知道了,下面有四种情况:
这样,堆块合并的问题就解决了。