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 }

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

posted on   UBless  阅读(766)  评论(0编辑  收藏  举报

编辑推荐:
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
阅读排行:
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· PPT革命!DeepSeek+Kimi=N小时工作5分钟完成?
· What?废柴, 还在本地部署DeepSeek吗?Are you kidding?
· DeepSeek企业级部署实战指南:从服务器选型到Dify私有化落地
· 程序员转型AI:行业分析

导航

< 2025年2月 >
26 27 28 29 30 31 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 1
2 3 4 5 6 7 8
点击右上角即可分享
微信分享提示