STL-空间配置器(allocator)

来源:互联网 发布:mvc php路由文件加载 编辑:程序博客网 时间:2024/05/16 09:52

SGI空间配置器

allocator的标准接口

allocator::value_typeallocator::pointerallocator::const_pointerallocator::referenceallocator::const_referenceallocator::size_typeallocator::diffrence_typeallocator::rebind一个嵌套的class template。class rebind<U>拥有唯一成员other,那是一个typedef,代表allocator<U>allocator::allocator()allocator::allocator(const allocator&)template<class U>allocator::allocator(const allocator<U>&)allocator::~allocator()pointer allocator::address(reference x) const返回某个对象的地址。算式a.address(x)等同于&xconst_pointer allocator::address(const_reference x) constpointer allocator::allocate(size_type n, const void* = 0)void alloctor::deallocate(pointer p, size_type n)size_type allocator::max_size() const返回可成功配置的最大量void allocator::construct(pointer p, const T& x)等同于 new(const void*)p T(x)void allocator::destroy(pointer p)等同于p->~T()

SGI的空间配置器std::alloc

SGI定义了一个名为allocator的配置器,但未使用,而是用了一个名为alloc的配置器。STL allocator中内存配置操作由alloc::allocate()负责,内存释放由alloc::deallocate()负责;对象构造由::construct()负责,对象析构由::destroy()负责。

STL中关于内存分配的相关头文件关系如下图所示

构造和析构工具construct和destroy

构造和析构的两个函数construct和destroy在stl_construct.h文件中,其主要结构如下

construct采用了placement new运算子完成,而destroy则有两个版本,一个就接受一个指针,然后调用其析构函数将其析构掉,另一个接受两个迭代器,将两个迭代器所指范围内的所有对象析构,同时还利用__type_traits判断该型别的析构函数是否trivial,是的话则什么都不做。

空间的分配与释放 std::alloc

对于空间的分配和释放,SGI主要是这样做的:

  • 向system heap要求空间
  • 考虑多线程状态
  • 考虑内存不足时的应变措施
  • 考虑过多的小块内存要求而造成的内存碎片问题

针对内存碎片问题,SGI设计了双层级配置器,第一级配置器就直接使用了malloc和free,第二级配置器就视情况采用不同的策略,当配置内存超过128bytes时,则调用第一级配置器,若小于128bytes,便采用内存池方式。

源码中第一级配置器为__malloc_alloc_template,第二级配置器为__default_alloc_template,通过宏来选择定义alloc为哪一级配置器。默认情况下都为第二级

typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;

同时,SGI还定义了一个simple_alloc,封装了allocator所需要的接口,STL的容器都使用这个接口。

template<class _Tp, class _Alloc>class simple_alloc {public:    static _Tp* allocate(size_t __n)      { return 0 == __n ? 0 : (_Tp*) _Alloc::allocate(__n * sizeof (_Tp)); }    static _Tp* allocate(void)      { return (_Tp*) _Alloc::allocate(sizeof (_Tp)); }    static void deallocate(_Tp* __p, size_t __n)      { if (0 != __n) _Alloc::deallocate(__p, __n * sizeof (_Tp)); }    static void deallocate(_Tp* __p)      { _Alloc::deallocate(__p, sizeof (_Tp)); }};

两级配置器的选择和应用关系如下图

第一级配置器__malloc_alloc_template

从源码中可以看到,第一级配置器的allocate,reallocate,deallocate分别调用了malloc,remalloc和free,同时模拟了C++的set_new_handler机制,在内存不足的时候调用由客户端设定的函数,否则直接抛出bad_malloc异常。在内存不足的情况下,allocate和reallocate分别调用了_S_oom_malloc和_S_oom_realloc函数来处理。_S_oom_malloc函数的源码如下

template <int __inst>void* __malloc_alloc_template<__inst>::_S_oom_realloc(void* __p, size_t __n){    void (* __my_malloc_handler)();    void* __result;    for (;;) {        __my_malloc_handler = __malloc_alloc_oom_handler;        if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }        (*__my_malloc_handler)();        __result = realloc(__p, __n);        if (__result) return(__result);    }}

第二级配置器__default_alloc_template

第二级配置器分配空间时会做一个判断,如果分配的空间大于128bytes,则直接调用第一级分配器,如果小于128bytes,就会采用第二级分配器的内存池管理技术。

第二级的内存池管理主要维护了16个自由空间链表,每个链表依次管理8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128bytes的内存块,每次分配空间时,先把空间大小上调到8的整数倍,然后到相应的链表中去查询是否有空闲的块可用,如果有,则从链表中取出相应的块,(没有的情况后面再介绍);相应的,释放的时候,就将它放回到相应的链表中。

比如下图所示,要分配一个56bytes的空间,首先定位到存储56bytes空闲块的链表头,发现它并非null,那么它指向的第一块空间就是我们所需要的结果(图中红色的块),在取出红色的块之后,我们需要把链表头指向它的下一块(图中虚线所示)。

释放空间时,也一样,先定位到链表头,然后将回收的块作为新的链表头,原来的块作为它的后继。

下面讨论当allocate时发现所处的链表中没有空闲块时,该如何处理。这个时候,就调用了_S_refill()函数,该函数从内存池去取新的空间,默认取得20个新节点,如果内存池空间不足,可能会小于20。内存池主要维护了三个变量:

static char* _S_start_free;static char* _S_end_free;static size_t _S_heap_size;

分别表示内存池当前的起始地址,结束地址和到目前为止,总共分配的堆的大小。到内存池取新的空间时,主要看内存池的剩余空间是否足够(_S_end_free-_S_start_free),然后分别处理。主要工作由_S_chunk_alloc()完成,总的流程如下:

内存基本处理工具

这里的内存处理工具主要是stl_uninitialized.h文件中的uninitialized_copy,uninitialized_fill函数。

uninitialized_copy

uninitialized_copy有两个针对char*和wchar_t*的特化版本,这两个版本都是直接调用了memmove函数;泛化版本如下所示

template <class _InputIter, class _ForwardIter>inline _ForwardIteruninitialized_copy(_InputIter __first, _InputIter __last,                     _ForwardIter __result){    return __uninitialized_copy(__first, __last, __result,                              __VALUE_TYPE(__result));}template <class _InputIter, class _ForwardIter, class _Tp>inline _ForwardIter__uninitialized_copy(_InputIter __first, _InputIter __last,                     _ForwardIter __result, _Tp*){      typedef typename __type_traits<_Tp>::is_POD_type _Is_POD;      return __uninitialized_copy_aux(__first, __last, __result, _Is_POD());}

泛化版本通过判断当前迭代器是否是无关紧要的(一些int等内置类型),如果是,则调用copy函数,否则,通过construct在新空间上一步步构造完成。

template <class _InputIter, class _ForwardIter>inline _ForwardIter __uninitialized_copy_aux(_InputIter __first, _InputIter __last,                         _ForwardIter __result,                         __true_type){  return copy(__first, __last, __result);}template <class _InputIter, class _ForwardIter>_ForwardIter __uninitialized_copy_aux(_InputIter __first, _InputIter __last,                         _ForwardIter __result,                         __false_type){  _ForwardIter __cur = __result;  __STL_TRY {    for ( ; __first != __last; ++__first, ++__cur)      construct(&*__cur, *__first);    return __cur;  }  __STL_UNWIND(destroy(__result, __cur));} 

uninitialized_fill

和uninitialized_copy类似,uninitialized_fill会根据当前迭代器的型别分别做不同的处理,如果是无关紧要的类型,就直接调用fill函数,否则,就用construct一步步构造。

template <class _ForwardIter, class _Tp>inline void__uninitialized_fill_aux(_ForwardIter __first, _ForwardIter __last,  const _Tp& __x, __true_type){  fill(__first, __last, __x);}template <class _ForwardIter, class _Tp>void__uninitialized_fill_aux(_ForwardIter __first, _ForwardIter __last,  const _Tp& __x, __false_type){  _ForwardIter __cur = __first;  __STL_TRY {for ( ; __cur != __last; ++__cur)  construct(&*__cur, __x);  }  __STL_UNWIND(destroy(__first, __cur));}template <class _ForwardIter, class _Tp, class _Tp1>inline void __uninitialized_fill(_ForwardIter __first,  _ForwardIter __last, const _Tp& __x, _Tp1*){  typedef typename __type_traits<_Tp1>::is_POD_type _Is_POD;  __uninitialized_fill_aux(__first, __last, __x, _Is_POD());}template <class _ForwardIter, class _Tp>inline void uninitialized_fill(_ForwardIter __first,   _ForwardIter __last,    const _Tp& __x){  __uninitialized_fill(__first, __last, __x, __VALUE_TYPE(__first));}
0 0
原创粉丝点击