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 }
三个函数实现了不同的功能,它们都用于未初始化的空间。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· PPT革命!DeepSeek+Kimi=N小时工作5分钟完成?
· What?废柴, 还在本地部署DeepSeek吗?Are you kidding?
· DeepSeek企业级部署实战指南:从服务器选型到Dify私有化落地
· 程序员转型AI:行业分析