本文主要讲讲 GNU-C++ 4.9 下的扩展内存分配器
首先 GNU-C++ 4.9 有 7 个扩展的内存分配器
new_allocator malloc_allocator pool_allocator __mt_alloc array_allocator debug_allocator bitmap_allocator 主要看看 pool_allocator array_allocator bitmap_allocator
以下源码都可在 .../ext/*.h 下找到 我将其进行了适当的 删减和修改
__gnu_cxx::new_allocator 直接用 ::operator new 和 ::operator delete 实现出来的 好处是 可以被重载 没啥特色
1 2 3 4 5 6 7 8 9 10 template <typename  _Tp>class  new_allocator  {public :    pointer allocate (size_type __n, const  void * = 0 )   {         return  static_cast <_Tp*>(::operator  new (__n * sizeof (_Tp)));     }     void  deallocate (pointer __p, size_type)           ::operator  delete (__p)      } }; 
__gnu_cxx::malloc_allocator 直接用 std::malloc 和 std::free 实现的 好处是 少调用 一次 operator new 和 operator delete 函数 节省一次入栈出栈开销 也没啥特色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 template <typename  _Tp>class  malloc_allocator  {public :    pointer allocate (size_type __n, const  void * = 0 )   {         if  (__n > this ->max_size ()) {             std::__throw_bad_alloc();         }         pointer __ret = static_cast <_Tp*>(std::malloc (__n * sizeof (_Tp)));         if  (!__ret) {             std::__throw_bad_alloc();         }         return  __ret;     }          void  deallocate (pointer __p, size_type)           std::free (static_cast <void *>(__p));     } }; 
__gnu_cxx::pool_allocator G2.9 容器默认配置器 在 我的 C++ 内存管理 之 STL内存分配实现原理  这篇文章中 的 GNU-C++ 2.9 std::alloc 原理 已经分析过了
__gnu_cxx::__mt_alloc 适用于多线程的内存分配
__gnu_cxx::array_allocator 用于分配固定大小的内存块 使用 标准库 中的 std::array 实现 无需再去调用 ::operator new 和 ::operator delete 在进入 main 之前就可以用了 因为是用的 静态数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 template <typename  _Tp, typename  _Array = std::tr1::array<_Tp, 1 > >class  array_allocator : public  array_allocator_base<_Tp> {public :    typedef  _Array  array_type; private :         array_type*     _M_array;          size_type       _M_used; public :         array_allocator (array_type* __array = 0 ) throw ()      : _M_array(__array), _M_used(size_type ()) { }     pointer allocate (size_type __n, const  void * = 0 ) {         if  (_M_array == 0  || _M_used + __n > _M_array->size ()) {             std::__throw_bad_alloc();         }         pointer __ret = _M_array->begin () + _M_used;         _M_used += __n;         return  __ret;     } }; 
要注意的是 array 因为是静态的 所以不需要 释放 因此 你调用 deallocate 是没有任何操作的
不过 如果说 deallocate 能够回收掉已经分出去的数组中的某块的话 那可能利用率更高一些
1 2 3 4 5 6 7 8 9 10 11 12 int  my[65536 ];array_allocator<int , array<int , 65536>> myAlloc (&my); int  *p = myAlloc.allocate (3 );myAlloc.deallocate (p); typedef  ARRAY std::array<int , 65536>;ARRAY *pa = new  ARRAY; array_allocator<int , ARRAY> myAlloc (pa)  ;
__gnu_cxx::debug_allocator 不做分配归还动作 里面传入一个真正的 allocator 来分配归还内存 正如其名 不做事情 只是用来 debug 用的 每次申请 多申请一块 来记录 分配的内存 然后归还的时候 assert 来查看 分配的 size 是否正确
有点类似 malloc 中的 cookie
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 template <typename  _Alloc>class  debug_allocator  {public :    typedef  typename  _Traits::size_type      size_type;     typedef  typename  _Traits::value_type     value_type; private :    size_type 		_M_extra;       _Alloc			_M_allocator;          size_type _S_extra() {         const  size_t  __obj_size = sizeof (value_type);         return  (sizeof (size_type) + __obj_size - 1 ) / __obj_size;      } public :    debug_allocator (const  _Alloc& __a)     : _M_allocator(__a), _M_extra(_S_extra()) { }     pointer allocate (size_type __n)   {                  pointer __res = _M_allocator.allocate (__n + _M_extra);               size_type* __ps = reinterpret_cast <size_type*>(__res);         *__ps = __n;         return  __res + _M_extra;     }     void  deallocate (pointer __p, size_type __n)                    using  std::__throw_runtime_error;         if  (__p) {             pointer __real_p = __p - _M_extra;             if  (*reinterpret_cast <size_type*>(__real_p) != __n) {                 __throw_runtime_error("debug_allocator::deallocate wrong size" );             }             _M_allocator.deallocate (__real_p, __n + _M_extra);         } else  {             __throw_runtime_error("debug_allocator::deallocate null pointer" );         }     } }; 
__gnu_cxx::bitmap_allocator 使用 bitmap 来查找 被使用和未被使用的内存块
内部实现了一个 mini vector 和 普通的 vector 一样 会 两倍 成长
一整块称之为 Super Block 每一个 Block 相当于一个元素单位
1 2 3 4   Super Blocks Size     记录已使用的块数    位图数组 记录某块是否被使用        64个Blocks 被 mini vector 所管理 [|  Super Blocks Size  |  Use Counts  |  bitmap[1]  |  bitmap[0]  |  [1][][][][][][]...[][][][][][][][][]  |]                                                             1110                                                 <-bitmap 记录的方向     Super Blocks 使用的方向-> 
假设 此时在32位系统下 一个内存块为 8 字节 则 Super Blocks Size 为 = 4 + (4 * 2) + 8 * (64 * 8) = 524字节
如果 全回收了 就 按照 Super Block 的大小 顺序放到 free_list 中 其实也是一个 vector 并且下次分配规模减半 (因为vector 每次分配都是两倍递增) 当 free_list 超过64个 Super Block 时 如果最后进来的 比最后面的还要大 就将其还给 OS (总而言之就是把最大的还给 OS)
如果 前面一个 Super Block 本来没区块 现在回收到了区块 此时又请求了区块的话 会从后面一个 Super Block 去分配 但是如果 后面一个 Super Block 不存在的话 则从 前面一个 Super Block 取
free_list 存在的意义就是 先把 Super Block 存起来 万一以后有用 就不用重新创建了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 template <typename  _Tp>class  bitmap_allocator  : private  free_list {public :    pointer allocate (size_type __n)   {         if  (__n > this ->max_size ()) { 	        std::__throw_bad_alloc();         }                           if  (__builtin_expect(__n == 1 , true )) {             return  this ->_M_allocate_single_object();         } else  {              const  size_type __b = __n * sizeof (value_type);             return  reinterpret_cast <pointer>(::operator  new (__b)); 	    }     }     void  deallocate (pointer __p, size_type __n)  throw ()           if  (__builtin_expect(__p != 0 , true )) {             if  (__builtin_expect(__n == 1 , true )) {                 this ->_M_deallocate_single_object(__p);             } else  {                 ::operator  delete (__p);             }         }     } };