STL 空间配置器

C++ 中使用new 和 delete来进行内存的申请和释放, new直接调用的是malloc来向操作系统申请内存。为了方便管理内存,操作系统分配出去的内存会带有其他附加信息,比如说cookies,保存了实际分配数据的个数,正是因为如此,在申请空间的时候需要指定申请的个数,而在释放空间的时候,不需要指定个数,只要给出new时候获得的指针变量就可以了。

现在问题来了,每次malloc都会带有cookies,一般是上下个有一个,每个4个字节,也就是说,如果你用new申请一个int类型变量,那么操作系统实际分配给你的有12个字节,拿到你手中的只有4个字节,占用比只有1/3。 这是一件很恐怖的事情。 为此,C++容器中的空间配置器没有采用这种粗暴的手法,虽然现在内存容量已经很大了。

相比较之下,VC6.0中容器的分配底层直接使用的就是malloc和free,没有任何的优化措施。

其实window下的malloc和free还是挺高效的,实际上并不是每次在你需要申请空间的时候才去向操作系统拿,是操作系统提前分配好了,并且也已经划分好了,底层的malloc和free还是挺高效的,扯远了,还是回到主题吧。 因为容器里放置的大小都是一样的,没有必要每个元素都带有两个cookie,因此从这个角度出发,才有了现在STL空间配置器模型。空间配置器模型的出现大部分是为了节省空间,分配效率上面malloc和free已经很高了其实。

内存配置操作:alloc:allocator  内存空间释放 alloc:deallocate()

对象构造    ::construct(), 对象析构  ::destory  两个全局函数

配置器定义于<memory>中 包含两个文件

#include <stl_alloc.h>  负责对象的配置和释放

#include <stl_construct.h>  负责对象内容的构造与析构

 用图综合表述大概就是这个样子的(参考了STL源码剖析)。

构造和析构内容解析

construct 直接调用的就是 placement new,调用构造函数在指定位置进行对象的构建。这部分已经没有什么可以优化的空间了。

destory有两个版本,一个是传递过来一个指针,直接调用析构函数。这个是最基本的,另外一个版本是用到了trivia手法,如果是class类型,就调用第一个版本,使用析构函数进行,如果是普通类型,就什么也不做。

空间配置和释放解析

STL 设置双层配置器,大于128字节 直接调用第一层,小于128字节,使用memory pool的方式来进行管理。

其实 如果是直接调用malloc的话,就没必要在封装第一层空间配置器,直接使用第二层空间配置器进行管理就可以了。比如GCC4.9版就只剩下了一级空间配置器,也就是memory pool的方式。

第二级空间配置器,维护16个自由链表,负责16中小型区块的配置,如果内存池不足,会调用第一层空间配置器来补充内存池。如果需求大于128字节,就直接调用第一级空间配置器。

具体流程是这样的。

1 将申请的大小调节到8的整数倍,同时获取应该是由哪一个链表的提供内存。

2 如果这个链表上面有内存,就直接返回。

3 如果没有,就看内存池有没有,如果内存池有 最多返回20个,最少返回一个,如果一个也不够,就将内存池中剩下的挂载到对应的链表 上面

4 这个时候 就使用malloc申请资源,申请的大小为 40 * 一个元素的字节 + 冗余。

5 如果申请成功,就返回第一个,将剩下的19个挂载到对应的链表上面,剩下的留在内存池中,

6 如果申请失败,就在自由链表上面尝试从前向后的上面进行搜索,直至搜索到,然后返回一个。

7 如果一个都没有搜多到,就调用newhandler来进行这种情况的处理。

猜你喜欢

转载自www.cnblogs.com/randyniu/p/9138885.html