- SGI 特殊的空间配置器 std::alloc
一般而言,我们习惯的C++内存配置操作和释放操作是这样的
class Foo{…..}
Foo* pf = new Foo;
delete pf;
new包含两阶段操作 (1)调用 ::operator new 配置内存 (2)调用 Foo::Foo() 构造对象内容。
delete也包含两阶段操作 (1)调用 Foo::~Foo() 将对象析构 (2)调用::operator delete 释放内存。
SGI<memory> 内含以下两个文件
#include<stl_alloc.h> //负责内存空间的配置与释放
#include<stl_construct.h> //负责对象内容的构造与析构
- 构造和析构基本工具: construct() 和 destroy()
destroy有两个版本,第一版本接受一个指针,直接调用该对象的析构函数。
第二版本接收 frist 和 last 两个迭代器, 将 [frist, last)范围内的所有对象析构掉。
首先利用 value_type () 获得迭代器所指对象的型别, 再利用 _type_traits<T> 判断该型别支持的析构函数是否是无关痛痒(trivial destructor ),若是,则什么也不做就结束,若不是,以循环寻访整个范围,并在循环中每经历一个对象就调用第一个版本的destroy()。
- 空间的配置与释放 std::alloc
对象构造前的空间配置和对象析构后的空间释放,由<stl_alloc.h>负责
(1)向system heap 要求空间
(2)考虑多线程状态
(3)考虑内存不足时的应变措施
(4)考虑过多"小型区块"可能造成的内存碎片问题。
考虑到小型区块所可能造成的内存破碎问题,SGI设计了双层级配置器。第一级配置器直接使用malloc() 和free(), 第二级配置器则视情况采用不同的策略:当配置区块超过 128bytes 时,视之为"足够大",便调用第一级配置器;当小于128bytes时,视之为"过小",为了降低额外的负担,采用复杂的 memory pool整理方式。
- 第一级配置器 __malloc_alloc_template
SGI 第一级配置器的 allocate() 和 realloc()都是在调用 malloc() 和 realloc() 不成功之后,改调用 oom_malloc() 和 oom_realloc() 两者都有内循环,不断调用"内存不足处理例程",期望在某次调用后获得足够的内存而圆满完成任务,但如果"内存不足处理例程"未被客户端设定,便会抛出异常。
- 第二级配置器 __default_alloc_template
第二级配置器多了一些机制,避免太多小额区块造成内存的碎片。小额区块带来的不仅仅是内存碎片的问题,配置时的额外负担也是很大的问题。系统要靠这多出来的空间来管理内存。区块愈小,额外负担所占比例就越大,就越浪费。
SGI第二级配置器的做法是,如果区块够大,超过128bytes时,就移交第一级配置器,当小于128bytes时,则以内存池(memory pool)管理,此法又称次层配置:
每次配置一大块内存,并维护之自由链表,下次若再有相同大小的内存需求,就直接从free-lists中拔出,如果客户端释还小额区块,就由配置器回收到free-lists中。 为了方便管理,SGI第二级配置器会主动将任何小额区块的内存需求量上调至8的倍数,并维护16个 free-lists,各自管理大小为8,16,24,32,40,56,64,72,80,88,96,104,112,120,128bytes
- 空间配置函数allocate()
- 空间释放函数deallocate()
- 重新填充 free lists
当它发现free lists中没有可用的区块时,就调用refill(),准备为free list 重新填充空间。新的空间将取自内存池。
- 内存基本处理工具
- uninitialized_copy()
uninitialized_copy()使我们能够将内存的配置与对象的构造行为分离开来。
如果作为输出目的地[result, result + (last - frist)) 范围内每一个迭代器都指向未初始化区域, 则 uninitiated_copy() 会使用 copy consturctor, 给身为输入来源之[frist, last] 范围内的每一对象产生一份复制品,放进输出范围中。
- Uninitialized_fill()
- Uninitialized_fill_n()
Uninitialized_fill_n()能够使我们将内存配置与对象构造行为分离开来,它会为指定范围内的所有元素设定相同初值
接受三个参数
(1)迭代器指向与初始化空间的起始处
(2)n 表示欲初始化空间的大小
(3)x 表示初值
首先萃取出迭代器Frist 的型别再判断是否是POD型别
POD指plain old data, 标量型别