zoukankan      html  css  js  c++  java
  • STL_空间配置器(allocators)

    空间配置器

    一般而言,我们使用C++ new操作符主要进行两步操作。

    • ::operator new分配内存
    • 调用构造函数构造对象

    而使用C++ delete操作符主要进行两步操作。

    • 调用析构函数析构对象
    • ::operator delete释放内存

    STL将内存分配与对象构造析构分开,因此空间配置器分为内存分配及空间初始化。

    SGI STL中的两级空间分配器

    第一级 __malloc_alloc_template

    108 template <int __inst>     
    109 class __malloc_alloc_template {
    110 
    111 private:
    112    
    113   static void* _S_oom_malloc(size_t);/*函数指针处理out of memory 情况*/
    114   static void* _S_oom_realloc(void*, size_t); 
    115    
    116 #ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
    117   static void (* __malloc_alloc_oom_handler)(); 
    118 #endif
    119    
    120 public:
    121    
    122   static void* allocate(size_t __n)
    123   {
    124     void* __result = malloc(__n); /*直接调用malloc()*/  
    125     if (0 == __result) __result = _S_oom_malloc(__n); /*处理OOM方式都相似,只列出allocate情况*/
    126     return __result;
    127   }
    128    
    129   static void deallocate(void* __p, size_t /* __n */)
    130   {
    131     free(__p);
    132   }
    133    
    134   static void* reallocate(void* __p, size_t /* old_sz */, size_t __new_sz) 
    135   {
    136     void* __result = realloc(__p, __new_sz);
    137     if (0 == __result) __result = _S_oom_realloc(__p, __new_sz); 
    138     return __result;
    139   }                       
    140   // 仿set_new_handler(只用于::operator new配置内存方式,malloc不能使用)
          // typedef void(*pf)() PF
          // PF __set_malloc_handler(void (*__f)())
          // __set_malloc_handler接收 void(*)()函数指针
          // 同时返回 void(*)()函数指针
    
    141   static void (* __set_malloc_handler(void (*__f)()))()
    142   {
    143     void (* __old)() = __malloc_alloc_oom_handler;
    144     __malloc_alloc_oom_handler = __f;
    145     return(__old);        
    146   }
    147
    148 };
    
    
    152 #ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
    153 template <int __inst>
    154 void (* __malloc_alloc_template<__inst>::__malloc_alloc_oom_handler)() = 0; /*初始化位空,需要用户指定*/
    155 #endif
    156 
    157 template <int __inst>
    158 void*
    159 __malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n)
    160 {
    161     void (* __my_malloc_handler)();
    162     void* __result;
    163 
    164     for (;;) { /*使用设定的handler处理,重复申请*/
    165         __my_malloc_handler = __malloc_alloc_oom_handler;
    166         if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; } /*如果没有设定直接抛出异常*/
    167         (*__my_malloc_handler)();
    168         __result = malloc(__n);
    169         if (__result) return(__result);/*如果成功则返回*/
    170     }
    171 }
    

    第一级非常简单,只是把malloc,free,realloc包装了一下,并提供一个类似set_new_handler的方法。用户需要自己设定handler,否则使用默认的throw std::bad_alloc()方式。

    第二级 __default_alloc_template

    第二级主要避免小额区块造成的内存碎片,以及频繁申请带来的负担。SGI的二级做法是如果区块大于128字节,移交第一级,否则以内存池管理,每次申请一块大内存,通过空闲链表(free-lists)分配和回收内存,二级配置会将小额区块的请求调到8的倍数。并维护16个free-lists各自管理大小为8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128的区块。

    304   union _Obj {
    305       union _Obj* _M_free_list_link;
    306       char _M_client_data[1];    /* The client sees this.        */
    307   };
    

    使用union结构体_Obj,第一个字段可视为指针,指向同一个free-list的下一个块,在freelist中用作链表;第二个字段可视为指向这一区块的指针,当申请到这一块内存后,p = *_Obj指向可用的内存。

    enum {_ALIGN = 8};
    enum {_MAX_BYTES = 128};
    enum {_NFREELISTS = 16};
    
    static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS]; //16个freelist
    
    299 static size_t _S_round_up(size_t __bytes) {//   将 size 上调为8的倍数
    300     return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1));
    301 }
    
    315   static  size_t _S_freelist_index(size_t __bytes) { // 返回size对应的freelist下标
    316         return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);
    317   }
    
    /*allocate -- 用户分配空间*/
    347   /* __n must be > 0      */
    348   static void* allocate(size_t __n)
    349   {
    350     void* __ret = 0;
    351 
    352     if (__n > (size_t) _MAX_BYTES) { //大于128,调用第一级
    353       __ret = malloc_alloc::allocate(__n);
    354     }
    355     else {
    356       _Obj* __STL_VOLATILE* __my_free_list
    357           = _S_free_list + _S_freelist_index(__n);//得到对应freelist下标
    358       // Acquire the lock here with a constructor call.
    359       // This ensures that it is released in exit or during stack
    360       // unwinding.
    361 #     ifndef _NOTHREADS
    362       /*REFERENCED*/
    363       _Lock __lock_instance; //多线程相关
    364 #     endif
    365       _Obj* __RESTRICT __result = *__my_free_list;
    366       if (__result == 0)//初次初始化||空间无剩余 
    367         __ret = _S_refill(_S_round_up(__n));//重新申请
    368       else {
    369         *__my_free_list = __result -> _M_free_list_link;//更新freelist列表,最后一块的_M_free_list_link设为0
    370         __ret = __result;//返回内存地址
    371       }
    372     }
    373 
    374     return __ret;
    375   };
    
    /*_S_refill --  填充freelist*/
    495 template <bool __threads, int __inst>
    496 void*
    497 __default_alloc_template<__threads, __inst>::_S_refill(size_t __n)
    498 {
    499     int __nobjs = 20; //分配__nobjs个区块
    500     char* __chunk = _S_chunk_alloc(__n, __nobjs);//__nobjs 为value-result参数  
    501     _Obj* __STL_VOLATILE* __my_free_list;
    502     _Obj* __result;
    503     _Obj* __current_obj;
    504     _Obj* __next_obj;
    505     int __i;
    506 
    507     if (1 == __nobjs) return(__chunk);//只有一块,直接返回
    508     __my_free_list = _S_free_list + _S_freelist_index(__n);
    509 
    510     /* 使用剩余内存构建该下标的freelist */
    511       __result = (_Obj*)__chunk;
    512       *__my_free_list = __next_obj = (_Obj*)(__chunk + __n);
    513       for (__i = 1; ; __i++) {
    514         __current_obj = __next_obj;
    515         __next_obj = (_Obj*)((char*)__next_obj + __n);
    516         if (__nobjs - 1 == __i) {
    517             __current_obj -> _M_free_list_link = 0;//最后一块设为0
    518             break;
    519         } else {
    520             __current_obj -> _M_free_list_link = __next_obj;
    521         }
    522       }
    523     return(__result);
    524 }
    
    /*_S_chunk_alloc -- 从内存池取一定数量区块*/
    427 template <bool __threads, int __inst>
    428 char*
    429 __default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size,
    430                                                             int& __nobjs)
    431 {
    432     char* __result;
    433     size_t __total_bytes = __size * __nobjs;
    434     size_t __bytes_left = _S_end_free - _S_start_free;/*内存池剩余空间*/
    435 
    436     if (__bytes_left >= __total_bytes) {//足够分配__nobjs个区块
    437         __result = _S_start_free;
    438         _S_start_free += __total_bytes;//分配好后,剩余的留给内存池,用作下次分配
    439         return(__result);
    440     } else if (__bytes_left >= __size) {//不够__nobjs块,剩下的全都分配来,更新__nobjs
    441         __nobjs = (int)(__bytes_left/__size);
    442         __total_bytes = __size * __nobjs;
    443         __result = _S_start_free;
    444         _S_start_free += __total_bytes;
    445         return(__result);
    446     } else {//否则重新申请
    447         size_t __bytes_to_get =
    448       2 * __total_bytes + _S_round_up(_S_heap_size >> 4);//申请两倍 + heap_size/16
    449         // 试试剩下的(必定为8的倍数)够不够小尺寸freelist使用
    450         if (__bytes_left > 0) {//将内存池中区块交给相应的free_list
    451             _Obj* __STL_VOLATILE* __my_free_list =
    452                         _S_free_list + _S_freelist_index(__bytes_left);
    453 
    454             ((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list;
    455             *__my_free_list = (_Obj*)_S_start_free;
    456         }
    457         _S_start_free = (char*)malloc(__bytes_to_get);
    458         if (0 == _S_start_free) {//malloc()失败,不会尝试小于__bytes_to_get的请求,出于效率以及减少内存碎片
    459             size_t __i;
    460             _Obj* __STL_VOLATILE* __my_free_list;
    461             _Obj* __p;
    462             // 从已经分配到的区块中搜寻。
    465             for (__i = __size;
    466                  __i <= (size_t) _MAX_BYTES;
    467                  __i += (size_t) _ALIGN) {
    468                 __my_free_list = _S_free_list + _S_freelist_index(__i);
    469                 __p = *__my_free_list;
    470                 if (0 != __p) {
    471                     *__my_free_list = __p -> _M_free_list_link;
    472                     _S_start_free = (char*)__p;
    473                     _S_end_free = _S_start_free + __i;//取出一块
    474                     return(_S_chunk_alloc(__size, __nobjs));//重新分配调整,修正__nobjs
    477                 }
    478             }
    479             _S_end_free = 0;    // 无论如何申请不到,调用第一级,试试OOM handler有没有用
    480             _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);
    481             
    484         }
    485         _S_heap_size += __bytes_to_get;
    486         _S_end_free = _S_start_free + __bytes_to_get;
    487         return(_S_chunk_alloc(__size, __nobjs));//修正__nobjs,将多余内存归还内存池
    488     }
    489 }
    
    /*deallocate() -- 用户释放空间*/
    378   static void deallocate(void* __p, size_t __n)
    379   {
    380     if (__n > (size_t) _MAX_BYTES) 
    381       malloc_alloc::deallocate(__p, __n);调用第一级
    382     else {
    383       _Obj* __STL_VOLATILE*  __my_free_list
    384           = _S_free_list + _S_freelist_index(__n);
    385       _Obj* __q = (_Obj*)__p;
    386    
    387       // acquire lock
    388 #       ifndef _NOTHREADS
    389       /*REFERENCED*/
    390       _Lock __lock_instance;
    391 #       endif /* _NOTHREADS */
    392       __q -> _M_free_list_link = *__my_free_list;
    393       *__my_free_list = __q;//将 __q区间插入对应freelist头部
    394       // lock is released here
    395     }
    396   }
    
    /*reallocate -- 重新分配空间*/
    526 template <bool threads, int inst>
    527 void*
    528 __default_alloc_template<threads, inst>::reallocate(void* __p,
    529                                                     size_t __old_sz,
    530                                                     size_t __new_sz)
    531 {   
    532     void* __result;
    533     size_t __copy_sz;
    534 
    535     if (__old_sz > (size_t) _MAX_BYTES && __new_sz > (size_t) _MAX_BYTES) {
    536         return(realloc(__p, __new_sz));//调用第一级
    537     }
    538     if (_S_round_up(__old_sz) == _S_round_up(__new_sz)) return(__p);
    539     __result = allocate(__new_sz);//申请
    540     __copy_sz = __new_sz > __old_sz? __old_sz : __new_sz;
    541     memcpy(__result, __p, __copy_sz);//拷贝
    542     deallocate(__p, __old_sz);//释放
    543     return(__result);
    544 }
    

    空间初始化

    内存分配完毕后,STL定义五个全局函数用于未初始化的空间。

    construct && destroy() -- 用于构造,析构对象

    #include <new.h> //使用placement new
    
     46 template <class _T1, class _T2>
     47 inline void _Construct(_T1* __p, const _T2& __value) {
     48   new ((void*) __p) _T1(__value);// T1::T1(__value)
     49 }  
     50    
     51 template <class _T1>      
     52 inline void _Construct(_T1* __p) {
     53   new ((void*) __p) _T1();
     54 }
    
     56 template <class _Tp>
     57 inline void _Destroy(_Tp* __pointer) {
     58   __pointer->~_Tp();
     59 }
    
     //接收迭代器,找出元素类别,根据__type_traits<>采取适当措施
     81 template <class _ForwardIterator>
     82 inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last) {
     83   __destroy(__first, __last, __VALUE_TYPE(__first));
     84 }
    
     61 template <class _ForwardIterator>
     62 void
     63 __destroy_aux(_ForwardIterator __first, _ForwardIterator __last, __false_type)//non-trivial destructor
     64 {
     65   for ( ; __first != __last; ++__first)
     66     destroy(&*__first);
     67 }
     68 
     69 template <class _ForwardIterator>
     70 inline void __destroy_aux(_ForwardIterator, _ForwardIterator, __true_type) {}//trivial destructor,不采取操作
    

    construct()通过指针p,可选的初值value,使用placement new运算子在对象构造在p所指的空间上。
    destroy()有两个版本,第一个接受一个指针;第二个接收迭代器,将[first,last)范围内的所有对象析构,首先使用 value_type获得对象类型,在进行trivial destructor的判定,如果析构函数是不重要的,destroy()最终不调用任何操作,否则才调用析构函数。

    uninitialized_fill_n, uninitialized_fill, unitialized_copy

    /*__uninitialized_fill_n : first:初始化空间起始,n:空间大小,x:初值*/
    205 template <class _ForwardIter, class _Size, class _Tp, class _Tp1>
    206 inline _ForwardIter
    207 __uninitialized_fill_n(_ForwardIter __first, _Size __n, const _Tp& __x, _Tp1*)
    208 {
    209   typedef typename __type_traits<_Tp1>::is_POD_type _Is_POD;
    210   return __uninitialized_fill_n_aux(__first, __n, __x, _Is_POD());
    211 }
    /*判断Tp1是否为POD(plain old data)类型,简单来说POD类型支持memcpy等类似的函数直接拷贝对象的二进制值。*/
    //是POD类型,fill_n通过赋值进行填充
    183 template <class _ForwardIter, class _Size, class _Tp>
    184 inline _ForwardIter
    185 __uninitialized_fill_n_aux(_ForwardIter __first, _Size __n,
    186                            const _Tp& __x, __true_type)
    187 { 
    188   return fill_n(__first, __n, __x);
    189 
    // 非POD,调用Construct。
    191 template <class _ForwardIter, class _Size, class _Tp>
    192 _ForwardIter
    193 __uninitialized_fill_n_aux(_ForwardIter __first, _Size __n,
    194                            const _Tp& __x, __false_type)
    195 { 
    196   _ForwardIter __cur = __first;
    197   __STL_TRY {
    198     for ( ; __n > 0; --__n, ++__cur)
    199       _Construct(&*__cur, __x);
    200     return __cur;
    201   }
    202   __STL_UNWIND(_Destroy(__first, __cur));//一旦发生异常全部析构
    203 }
    

    这三个函数都要求"commit or rollback"语义,要么产生所有元素,要么不产生任何元素。上面异常处理模块保证了这一语义。

    /*uninitlized_cpoy,first:起始位置,last:结束位置,result:初始化空间起始*/
     74 template <class _InputIter, class _ForwardIter>
     75 inline _ForwardIter
     76   uninitialized_copy(_InputIter __first, _InputIter __last,
     77                      _ForwardIter __result)         
     78 {
     79   return __uninitialized_copy(__first, __last, __result,
     80                               __VALUE_TYPE(__result));       
     81 }
    
     65 template <class _InputIter, class _ForwardIter, class _Tp> 
     66 inline _ForwardIter
     67 __uninitialized_copy(_InputIter __first, _InputIter __last,
     68                      _ForwardIter __result, _Tp*)   
     69 {  
     70   typedef typename __type_traits<_Tp>::is_POD_type _Is_POD;
     71   return __uninitialized_copy_aux(__first, __last, __result, _Is_POD());
     72 }
    
    //POD类型,调用copy,填充赋值
     40 template <class _InputIter, class _ForwardIter>
     41 inline _ForwardIter 
     42 __uninitialized_copy_aux(_InputIter __first, _InputIter __last,
     43                          _ForwardIter __result,         
     44                          __true_type)                   
     45 {  
     46   return copy(__first, __last, __result);
     47 }
    //非POD类型
     49 template <class _InputIter, class _ForwardIter>
     50 _ForwardIter 
     51 __uninitialized_copy_aux(_InputIter __first, _InputIter __last,
     52                          _ForwardIter __result,         
     53                          __false_type)                  
     54 {  
     55   _ForwardIter __cur = __result; 
     56   __STL_TRY {
     57     for ( ; __first != __last; ++__first, ++__cur) 
     58       _Construct(&*__cur, *__first); 
     59     return __cur;
     60   }
     61   __STL_UNWIND(_Destroy(__result, __cur));
     62 } 
    

    对于char* 和 wchar_t*两种类型,uninitialized_copy直接采用memmove直接移动内容

    //uninitialized_copy: first:初始化空间起始,last:初始化空间结束(前闭后开),x:初值
     74 template <class _InputIter, class _ForwardIter>
     75 inline _ForwardIter
     76   uninitialized_copy(_InputIter __first, _InputIter __last,
     77                      _ForwardIter __result)
     78 {
     79   return __uninitialized_copy(__first, __last, __result,
     80                               __VALUE_TYPE(__result));
     81 }
    
    //POD类型
    143 template <class _ForwardIter, class _Tp>
    144 inline void
    145 __uninitialized_fill_aux(_ForwardIter __first, _ForwardIter __last,
    146                          const _Tp& __x, __true_type)
    147 {
    148   fill(__first, __last, __x);
    149 }
    
    //非POD
    151 template <class _ForwardIter, class _Tp>
    152 void
    153 __uninitialized_fill_aux(_ForwardIter __first, _ForwardIter __last,
    154                          const _Tp& __x, __false_type)
    155 {
    156   _ForwardIter __cur = __first;
    157   __STL_TRY {
    158     for ( ; __cur != __last; ++__cur)
    159       _Construct(&*__cur, __x);
    160   }
    161   __STL_UNWIND(_Destroy(__first, __cur));
    162 }
    

    三个函数实现了不同的功能,它们都用于未初始化的空间。

  • 相关阅读:
    为什么页面设计宽度要控制在960px
    RRDtool运用
    cacti监控jvm
    cacti安装
    rConfig v3.9.2 授权认证与未授权RCE (CVE-2019-16663) 、(CVE-2019-16662)
    Linux安全学习
    Github-Dorks与辅助工具
    警方破获超大DDoS黑产案,20万个僵尸网络运营商被抓
    SRC漏洞挖掘
    威胁情报木马病毒样本搜集
  • 原文地址:https://www.cnblogs.com/zyfgs2012/p/4100168.html
Copyright © 2011-2022 走看看