STL空间配置器
考虑到小型区块可能造成的内存破碎问题,SGI 为此设计了双层级配置器。
- 当配置区块超过 128bytes 时,称为足够大,使用第一级配置器
malloc_alloc_template
,直接使用malloc()
和free()
。 - 当配置区块不大于 128bytes 时,为了降低额外负担,直接使用第二级配置器
default_alloc_template
,采用复杂的memory pool
处理方式。
维护16条链表,分别是0-15号链表,最小8字节,以8字节逐渐递增,最大128字节,传入一个字节参数,表示需要多大的内存,会自动校对到第几号链表(如需要13bytes空间,会分配 16bytes),在找到第n个链表后查看链表是否为空,如果不为空直接从对应的free_list中取出,将已经取出的指针向后移动一位。
若对应的free_list为空,先看其内存池是不是空时,如果内存池不为空:
(1)先检验它剩余空间是否够20个节点大小(即所需内存大小(提升后) * 20),若足够则直接从内存池 中拿出20个节点大小空间,将其中一个分配给用户使用,另外19个当作自由链表中的区块挂在相应的 free_list下,这样下次再有相同大小的内存需求时,可直接取出。
(2)如果不够20个节点大小,则看它是否能满足1个节点大小,如果够的话则直接拿出一个分配给用 户,然后从剩余的空间中分配尽可能多的节点挂在相应的free_list中。
(3)如果连一个节点内存都不能满足的话,则将内存池中剩余的空间挂在相应的free_list中(找到相应 的free_list),然后再给内存池申请内存,转到3。
内存池为空,申请内存。此时二级空间配置器会使用malloc()从heap上申请内存,(一次所申请的内存大小为2 * 所需节点内存大小(提升后)* 20 + 一段额外空间),申请40块,一半拿来用,一半放内存池中。
malloc没有成功。在第三种情况下,如果malloc()失败了,说明heap上没有足够空间分配给我们了,这时,二级空间配置器会从比所需节点空间大的free_list中一一搜索,从比它所需节点空间大的free_list中取出一个节点来使用。如果这也没找到,说明比其大的free_list中都没有自由区块了,那就要调用一级适配器了。
释放时调用deallocate()函数,若释放的n>128,则调用一级空间配置器,否则就直接将内存块挂上自由链 表的合适位置。
STL二级空间配置器虽然解决了外部碎片与提高了效率,但它同时增加了一些缺点:
- 因为自由链表的管理问题,它会把我们需求的内存块自动提升为8的倍数,这时若你需要1个字节,它 会给你8个字节,即浪费了7个字节,所以它又引入了内部碎片的问题,若相似情况出现很多次,就会造 成很多内部碎片;
- 二级空间配置器是在堆上申请大块的狭义内存池,然后用自由链表管理,供现在使用,在程序执行过 程中,它将申请的内存一块一块都挂在自由链表上,即不会还给操作系统,并且它的实现中所有成员全是静态的,所以它申请的所有内存只有在进程结束才会释放内存,还给操作系统,由此带来的问题有: 1.即我不断的开辟小块内存,最后整个堆上的空间都被挂在自由链表上,若我想开辟大块内存就会失败;2.若自由链表上挂很多内存块没有被使用,当前进程又占着内存不释放,这时别的进程在堆上申请不到空间,也不可以使用当前进程的空闲内存,由此就会引发多种问题。