空间配置器
详解SGI特殊的空间配置器-alloc
-
我们所习惯的c++内存操作:new/delete (new: 首先operator new 配置内存,再然后 调用构造函数构造对象;delete:先调用析构函数析构对象,在operate delete 释放内存)
- STL决定将上面的两个过程分开,内存配置操作由alloc::allocate()完成,释放由alloc::deallocate().对象构造由::construct()完成,对象析构由destroy()完成。在STL中,配置器定义在< memery >中,里面包含三个头文件<stl_construct.h>(
这里定义了全局函数construct()与destroy(),负责对象的构造与析构 )<stl_alloc.h>(这里定义了一,二级配置器,彼此合作,后面解释什么是一二级配置器 ),<stl_uninitialized.h>(这里面定义了一些全局函数,用来填充或复制大块内存数据:un_initialized_copy(),un_initialized_fill(),un_unitialized_fill_n() ) -
构造与析构函数基本工具:construct(),destroy() ; construct,接受一个指针与初值value,意为将初值设定到指针所指的空间上。;destroy()有两个版本,第一个接受一个指针,将指针所指之物析构掉,进而直接调用析构函数。第二个版本是接受一对迭代器,指向一段范围的空间,我们不清楚这个范围有多大,如果我们重复的调用析构函数,这将给效率造成负担。所以我们应该首先应该根据value_type() 去判断这个对象的类别,再根据(使用_type_traits< T >判断 )这个类别取判断该类别的析构函数是否无关痛痒。如果这些析构函数无关痛痒,那么就什么也不做,否则就一次次的重复调用析构函数。【这里的_type_traits< T > 是STL的编程技法。具体分析看我前面的文章解释,后面我也会详细的解析】 -
空间的配置与释放:为了解决小型区域可能造成的碎片问题,我们设置双层级配置器,第一级配置器,直接调用malloc()与free().第二季配置根据情况采用不同策略:当配置区过大(超过128bytes),我们就调用一级配置器,当配置区过小,为了降低负担,我们便采用memory pool 的整理方式,进而不采用第一级配置器。```SGI STL还为alloc包装了一个接口,让它的配置单位从bytes到元素的大小 template<class T,class Alloc>
class simple_alloc{
static T *allocate(size_t n){
return 0==n?0:(T*)Alloc::allocate(n*sizeof(T));
}
static T*allocate(void){
return (T*)Alloc::allocate(n*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));
}
};
//下面使vector调用alloc的过程
template<class T,class Alloc=alloc>
class vector{
typedef simple_alloc<value_type,Alloc>data_allocator;
void deallocate(){
if(...)
data_allocator::deallocate(...);
}
};
-
第一级配置器剖析
- 第一级配置器以malloc(),free(),remalloc()等c函数来进行内存的配置,释放,重配置。并实现出c++new handler的行为
- c++new handler机制:你可以要求系统在内存需要配置不被满足的时候,调用你所指定的一个函数。,也就是一旦operator new 无法完成时,会在抛出std::bad_allc之前,先调用由客端指定的例程,该例程用来解决内存不足的问题【
"内存不足处理例程"使客端的责任,如果,未设定,那么就会直接抛异常信息 】 - 一级配置器,在调用malloc(),与remalloc()不成功之后就会直接调用oom_malloc(),与oom_remalloc(),他们中有内循环,可以反复调用内存不足处理例程。以达到内存配置的目的,如果客端未设置例程那么,就会oom_malloc(),与oom_remalloc()就会抛出异常信息
-
第二级配置器剖析
-
二级配置级做法:如果所需区块过大,那么就交给第一级配置器处理,如果小于128bytes,则以内存池管理:每次配置一大块内存,并维护对应之自由链表。下次如果有相同大小的内存需求就直接拨出。如果客端还小额区块,就有配置器回收至自由链表中(free-lists),同时STL第二季配置器还会主动将内存需求量上升至8的倍数。 -
空间配置函数:身为第二级配置器,其含有allocate()函数,该函数首先判断区块大小,如果大于128bytes就调用第一级配置器,如果小于就寻找16个free_lists中的一个,如果没找到可用的区块,就会将区块大小上升至8的倍数,然后调用refill()函数为free-list填充空间 -
空间释放函数,该函数首先判断区块大小,如果大于128bytes就调用第一级配置器,如果小于,就寻找对应的free-lists,将区块回收。 -
内存池
-
内存基本处理工具(5个函数)
-
uninitialized_copy() : template<class InputIterator,class ForwardIterator>
ForwardIterator
uninitialized_copy(InputIterator first,InputIterator last,ForwardIterator result)
//result这个范围迭代器,指向一片未初始化的区域。uninitialized_copy()会调用construct函数将输入范围里的所有元素产生一份复制品放入输出范围中对应位置上。
//c++标准要求uninitialized_copy() 要么构造出所有的元素,要么什么也不构造,最适合用来实现一个容器。
-
uninitialized_fill() template<class ForwardIterator,class T>
void uninitialized_fill(InputIterator first,InputIterator last,const T&x)
//[firsst,last)指的是一片未初始化的区域,对于这个区域,这个函数会对于这个操作范围内的所有迭代器i,使用construct(&*i,x),在i所指之处产生x的复制品
//c++标准要求这个函数要么产生出所有的元素,要么什么也不要产生。
-
uninitialized_fill_n() template<class ForwardIterator,class Size,class T>
ForwardIterator
uninitialized_fill_n(ForwardIterator first,Size n,const T&x)
//在【first,first+n)这个范围内构造x的复制品,与上面的函数实现一样
另外两个函数上面已经介绍分别是construct(),与destroy().同时上面3个unin型函数会在底层调用更高级的函数:copy(),fill(),fill_n();
|