STL --- 空间配置器

本文深入探讨了STL中的空间配置器概念,解释了为何需要空间配置器而非直接申请空间,重点分析了内存碎片问题及性能考量。文章详细介绍了SGI-STL下空间配置器的构成,包括构造与析构、空间的配置与释放,以及第一级和第二级空间配置器的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言:

由于整个STL的操作对象都存放在容器之内,而容器一定需要配置空间来放置资料,所以引入了空间配置器。

思考:为什么要有空间配置器,而不直接在使用时申请空间?

      1. 内存空间碎片化问题

          一般在申请空间时,系统将分配足够使用的一段连续空间,由于频繁分配、释放尤其是小块内存,就更容易造成空间碎片的问题(危害:即使内存空间刚好满足一个请求,但由于内存空间分散,导致任意单独的内存块都不能被分配,从而造成内存利用率低)。而空间配置器是通过内存池的方式去管理,尤其适合对小块内存的管理。

       2. 性能问题

我们能每次在申请空间时,分配器都要去找一块空闲块给用户,延伸上一问题如果空间碎片较多,那势必分配器要花更多时间去匹配合适的空闲块,假如差的情况下没有单独合适大小的空闲块,就要考虑合并哪些小的空闲块,而这也需要花费时间处理。

另外,malloc在开辟空间时,要对这些内存块添加额外信息,如果频繁申请小块内存,这也会造成较大的负担。

 

空间配置器:

注:这里所述皆为SIG-STL的空间配置器。

    空间配置器的构成:

  

  •  构造与析构

这里的构造与析构函数属于STL标准规范。

原理:

 

  •  空间的配置与释放

之前已经介绍过alloc可以是第一级空间配置器或是第二级空间配置器,但在SGI-STL下默认为第二级空间配置器。 除此之外,为alloc封装了一个接口(simple_alloc),在调用时就可以使用此接口,而不论alloc是哪一级都以模板参数方式传给simple_alloc(顺便提醒:alloc不接受任何template型别参数)。

定义alloc:

// 将alloc定义为第一级配置器
    typedef __malloc_alloc_template<0> malloc_alloc;
    typedef malloc_alloc alloc;

// 将alloc定义为第二级配置器
    typedef __default_alloc_template<0,0> alloc;

 

接口封装定义如下:

template<class T, class Alloc>
class simple_alloc{
public:
    static T* allocate(size_t n)
        {return 0==n ? 0 : (T*) Alloc::allocate(n*sizeof(T));}
    static T* allocate(void)
        {return (T*) Alloc::allocate(sizeof(T));}
    static void deallocate(T* p, size_t n)
        {if(0 != n) Alloc::deallocate(p, n*sizeof(T));}
    static void deallocate(T* p)
        {Alloc::deallocate(p, sizeof(T));}

}
 

第一级空间配置器:

当第二级配置器没有足够空间时,就会求助第一级空间配置器。第一级空间配置器的处理流程:

 

其使用完的直接通过free释放。

 

第二级配置器:

之前说过第一级配置器是在第二级配置器空间不足时调用,而第二级配置器的最大能力是128字节

我们知道第二级配置器是用内存池进行管理的,其管理方式也叫做次层配置:用自由链表来管理一块的内存。如果有大小相同的内存需求则直接从链表中取出,当使用结束后,配置器将释放的空间还回链表。

配置器会将每次请求的内存升至为8的倍数。虽然这样做依然会造成部分空间浪费,但却大大方便了我们的管理,因为只需要维护16个free-list就可以了。另外,为了节省空间,链表节点被设置为union:

union obj{
    union obj* free_list_link; // 指向自由链表节点的指针
    char client_data[1];  // 指向实际区块
}

配置器是通过free_list[16]这样的指针数组维护这16个自由链表的:

数组每个元素代表一个指针指向相应链表的地址: 

这里有一疑问,obj第二成员为什么是char client_data[1]?

   首先理解union类型的特性:

  • union类型大小是其成员中最大的类型大小;
  • 成员共用同一块内存空间而且可作为成员中的任意一种类型来使用;
  • union的存放顺序是所有成员都从低地址开始存放。

   在就我个人理解:obj应该是4字节大小,对于free_list_link而言存放的是下一节点地址。而对client_data[1],它的作用是能通过其数组首地址来确定obj或是说此区块的起始位置的。

 

第二级空间配置器处理流程:

    由于第二级空间配置情况较多在此分情况讨论:

  •  在链表中刚好有所要请求大小(经过上调至8倍数,之后不在赘述)的内存块:

         采用类似头删的方式取出内存块。

  •  与请求块大小相等的区块链表为空时:

         链表没有可用块时,向内存中申请填充:

  •  相应链表和内存池都为空时:

  •  堆区也没内存:

 

空间配置器缺点:

      1.  空间配置器所有的函数和变量都是静态的,所以只有程序结束时才会释放,导致自由链表上的内存块只能自己进程使用,其他进程用不了;

      2.  第二级配置器依然会产生内存碎片,造成内存浪费;

      3.  第一级配置器中,若客端对内存不足处理方式不当,可能造成死循环。

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值