STL的两级空间配置器

首先抛出一个问题:为什么需要二级配置器?
因为当我们动态分配内存的时候,分配的内存往往不仅仅是我们需要的那些,还会产生一些额外的开销,比如首尾的cookies,debug模式下产生的额外开销,和内存对齐所产生的pad。这些附加信息,降低了空间的利用率。

于是就设置了二级空间配置器,当开辟小等于128bytes内存时,就视为开辟小块内存,调用二级空间配置器。否则调用一级空间配置器。

一级空间配置器

在一级空间配置器中,最重要的函数有:

  • allocate:用于分配空间,申请失败,调用oom_alloc尝试重新申请
  • deallocate:用于释放空间
  • reallocate:调整已经存在的空间大小,如果调整失败,调用oom_alloc尝试重新申请

其实对应的标准库函数就是malloc,free和realloc。

二级空间配置器

  1. 维护了16条单向链表,最小8字节,以8字节逐步递增到128字节。系统会自动根据你要分配的内存大小来向上对齐到符合的第一个链表。若不为空,则直接从对应链表中拔出,并且指针向后一位。
  2. 对应的free_list为空,先看其内存池是不是空时,如果内存池不为空:
    • 先检验它剩余空间是否够20个节点大小(即所需内存大小(提升后) * 20),若足够则直接从内存池中拿出20个节点大小空间,将其中一个分配给用户使用,另外19个当作自由链表中的区块挂在相应的free_list下,这样下次再有相同大小的内存需求时,可直接拨出。
    • 如果不够20个节点大小,则看它是否能满足1个节点大小,如果够的话则直接拿出一个分配给用户,然后从剩余的空间中分配尽可能多的节点挂在相应的free_list中。
    • 如果连一个节点内存都不能满足的话,则将内存池中剩余的空间挂在相应的free_list中(找到相应的free_list),然后再给内存池申请内存,转到3。
  3. 内存池为空,申请内存
    此时二级空间配置器会使用malloc()从heap上申请内存,(一次所申请的内存大小为2 所需节点内存大小(提升后) 20 + 一段额外空间),申请40块,一半拿来用,一半放内存池中。
  4. malloc没有成功
    在第三种情况下,如果malloc()失败了,说明heap上没有足够空间分配给我们了,这时,二级空间配置器会从比所需节点空间大的free_list中一一搜索,从比它所需节点空间大的free_list中拔除一个节点来使用。如果这也没找到,说明比其大的free_list中都没有自由区块了,那就要调用一级适配器了。

释放时调用deallocate()函数,若释放的n>128,则调用一级空间配置器,否则就直接将内存块挂上自由链表的合适位置。

STL二级空间配置器虽然解决了外部碎片与提高了效率,但它同时增加了一些缺点:

  1. 因为自由链表的管理问题,它会把我们需求的内存块自动提升为8的倍数,这时若你需要1个字节,它会给你8个字节,即浪费了7个字节,所以它又引入了内部碎片的问题,若相似情况出现很多次,就会造成很多内部碎片;

  2. 二级空间配置器是在堆上申请大块的狭义内存池,然后用自由链表管理,供现在使用,在程序执行过程中,它将申请的内存一块一块都挂在自由链表上,即不会还给操作系统,并且它的实现中所有成员全是静态的,所以它申请的所有内存只有在进程结束才会释放内存,还给操作系统,由此带来的问题有:1.即我不断的开辟小块内存,最后整个堆上的空间都被挂在自由链表上,若我想开辟大块内存就会失败;2.若自由链表上挂很多内存块没有被使用,当前进程又占着内存不释放,这时别的进程在堆上申请不到空间,也不可以使用当前进程的空闲内存,由此就会引发多种问题。

作者

Benboby

发布于

2021-03-01

更新于

2021-03-28

许可协议

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×