• 8、STL的两级空间配置器


    1、首先明白为什么需要二级空间配置器?

    我们知道动态开辟内存时,要在堆上申请,但若是我们需要 频繁的在堆开辟释放内存,则就会在堆上造成很多外部碎片,浪费了内存空间; 每次都要进行调用malloc、free函数等操作,使空间就会增加一些附加信息,降低了空间利用率; 随着外部碎片增多,内存分配器在找不到合适内存情况下需要合并空闲块,浪费了时间,大大降低了效 率。 于是就设置了二级空间配置器,当开辟内存<=128bytes时,即视为开辟小块内存,则调用二级空间配置 器。 关于STL中一级空间配置器和二级空间配置器的选择上,一般默认选择的为二级空间配置器。 如果大于 128字节再转去一级配置器器。

    一级配置

    一级空间配置器中重要的函数就是allocate、deallocate、reallocate 。 一级空间配置器是以malloc(), free(),realloc()等C函数执行实际的内存配置 。大致过程是: 1、直接allocate分配内存,其实就是malloc来分配内存,成功则直接返回,失败就调用处理函数 2、如果用户自定义了内存分配失败的处理函数就调用,没有的话就返回异常 3、如果自定义了处理函数就进行处理,完事再继续分配试试

    二级配置器

    1、维护16条链表,分别是0-15号链表,最小8字节,以8字节逐渐递增,最大128字节,你传入一个字节 参数,表示你需要多大的内存,会自动帮你校对到第几号链表(如需要13bytes空间,我们会给它分配 16bytes大小),在找到第n个链表后查看链表是否为空,如果不为空直接从对应的free_list中拔出,将已 经拨出的指针向后移动一位。

    2、对应的free_list为空,先看其内存池是不是空时,如果内存池不为空:

     (1)先检验它剩余空间是否够20个节点大小(即所需内存大小(提升后) * 20),若足够则直接从内存池 中拿出20个节点大小空间,将其中一个分配给用户使用,另外19个当作自由链表中的区块挂在相应的 free_list下,这样下次再有相同大小的内存需求时,可直接拨出。

    (2)如果不够20个节点大小,则看它是否能满足1个节点大小,如果够的话则直接拿出一个分配给用户,然后从剩余的空间中分配尽可能多的节点挂在相应的free_list中。

    (3)如果连一个节点内存都不能满足的话,则将内存池中剩余的空间挂在相应的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.若自由链表上挂很多内存块没有被使用,当前进程又占着内存不释放,这时别的进程在堆上申请 不到空间,也不可以使用当前进程的空闲内存,由此就会引发多种问题。

    一级分配器

    GC4.9之后就没有第一级了,只有第二级

    二级分配器

    ——default_alloc_template 剖析

    有个自动调整的函数:你传入一个字节参数,表示你需要多大的内存,会自动帮你校对到第几号链表(0-15号链表,最小8字节 最大128字节)

    allocate函数:如果要分配的内存大于128字节,就转用第一级分配器,否则也就是小于128字节。那么首 先判断落在第几号链表,定位到了,先判断链表是不是空,如果是空就需要充值,(调节到8的倍数, 默认一次申请20个区块,当然了也要判断20个是不是能够申请到,如果只申请到一个那就直接返回好 了,不止一个的话,把第2到第n个挨个挂到当前链表上,第一个返回回去给容器用,n是不大于20的,当然了如果不在1-20之间,那就是内存碎片了,那就先把碎片挂到某一条链表上,然后再重新malloc了, malloc 2*20个块)去内存池去拿或者重新分配。不为空的话

  • 相关阅读:
    Spark Streaming反压机制
    spark submit参数及调优
    kafka中的配额管理(限速)机制
    Kafka限流
    BlockingQueue
    Android 更改userdata的大小
    visual studio 2019 error MSB3073 exited with code 1
    ARM 链接脚本分析
    Android Bluetooth hci 命令分析
    Android 查看蓝牙hci日志
  • 原文地址:https://www.cnblogs.com/crbhf/p/15068587.html
Copyright © 2020-2023  润新知