C++ STL相关知识整理(一) allocator

内存配置和对象构造分开

一般而言,我们习惯的 C++ 内存配置操作和释放操作是这样的:

class FOO{};
FOO *pf = new FOO;    
delete pf;

  我们看其中第二行和第三行,虽然都是只有一句,当是都完成了两个动作。但你 new 一个对象的时候两个动作是:先调用::operator new 分配一个对象大小的内存,然后在这个内存上调用FOO::FOO()构造对象。同样,当你 delete 一个对象的时候两个动作是:先调用FOO::~FOO() 析构掉对象,再调用::operator delete将对象所处的内存释放。为了精密分工,STL 将allocator决定将这两个阶段分开。分别用 4 个函数来实现:

  1.内存的配置:alloc::allocate();

  2.对象的构造:::construct();

  3.对象的析构:::destroy();

  4.内存的释放:alloc::deallocate();

template <class T1, class T2>
inline void construct(T1* p, const T2& value) {
   new (p) T1(value);
}

其中construct使用了placement new

内存碎片

在SGI版本的STL中,空间的配置释放都由< stl_alloc.h > 负责。它的设计思想如下:

  • 向system heap要求空间
  • 考虑多线程
  • 考虑内存不足的应变措施
  • 考虑内存碎片的问题

 两级配置器

考虑到小型区块可能造成内存破碎问题(即形成内存碎片),SGI STL 设计了双层级配置器。第一层配置器直接使用malloc() 和 free().第二层配置器则视情况采用不同的策略:但配置区块超过 128 bytes时,调用第一级配置器。当配置区块小于 128 bytes时,采用复杂的 memory pool 方式。

内存池和自由链表

如上图所示,自由链表是一个指针数组,有点类似与hash桶,它的数组大小为16,每个数组元素代表所挂的区块大小,比如free _ list[0]代表下面挂的是8bytes的区块,free _ list[1]代表下面挂的是16bytes的区块…….依次类推,直到free _ list[15]代表下面挂的是128bytes的区块

同时我们还有一个被称为内存池地方,以start _ free和 end _ free记录其大小,用于保存未被挂在自由链表的区块,它和自由链表构成了伙伴系统。

分配与回收

 

如果用户需要是一块n字节的区块,且n <= 128(调用第二级配置器),此时Refill填充是这样的:(需要注意的是:系统会自动将n字节扩展到8的倍数也就是RoundUP(n),再将RoundUP(n)传给Refill)用户需要n块,且自由链表中没有,因此系统会向内存池申请nobjs * n大小的内存块,默认nobjs=20

  • 如果内存池大于 nobjs * n,那么直接从内存池中取出
  • 如果内存池小于nobjs * n,但是比一块大小n要大,那么此时将内存最大可分配的块数给自由链表,并且更新nobjs为最大分配块数x (x < nobjs)
  • 如果内存池连一个区块的大小n都无法提供,那么首先先将内存池残余的零头给挂在自由链表上,然后向系统heap申请空间,申请成功则返回,申请失败则到自己的自由链表中看看还有没有可用区块返回,如果连自由链表都没了最后会调用一级配置器

参考资料:STL空间配置器allocator详解_xy913741894的博客-CSDN博客_空间配置器

浅析STL allocator_teslali-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值