STL之空间配置器源码(框架)剖析

本文深入剖析了STL中的空间配置器实现细节,包括一级和二级空间配置器的工作原理及内部机制。介绍了一级配置器如何直接使用系统资源进行内存分配与释放,以及二级配置器如何通过更高效的内存管理策略提高性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

stl_alloc.h
  1
  2 //一级空间配置器
  3
  4 #if 0
  5 #   include<new>
  6 #   define __THROW_BAD_ALLOC throw bad_alloc
  7 #elif !defined(__THROW_BAD_ALLOC)
  8 #include<iostream>
  9 using namespace std;
 10 #define __THROW_BAD_ALLOC cerr<<"out of memory"<<endl; exit(1)
 11 #endif
 12
 13 //malloc-based allocator 通常比稍后介绍的default alloc速度慢
 14 //一般而言是thread-safe 并且对于空间的运用比较高效(efficient)
 15 //以下是第一级配置器
 16 //注意。无"无template型别参数",至于"非型别参数"inst,则完全没派上用场
 17
 18 template<int inst>
 19 class __malloc_alloc_template
 20 {
 21 private:
 22     //以下函数用来处理内存不足的情况
 23     //oom : out of memory
 24     static void* oom_malloc(size_t);
 25     static void* oom_realloc(void*,size_t);
 26     static void (*__malloc_alloc_oom_handler) ();
 27
 28 public:
 29     static void* allocate(size_t n)
 30     {
 31         void* result = malloc(n);//第一级配置器直接使用malloc()
 32         //当需求无法被满足时,改用oom_malloc()
 33         if(0 == result);
 34             result = oom_malloc(n);
 35         return result;
 36     }
 37
 38     static void deallocate(void* p,size_t n)//第一级配置器直接使用free();
 39     {
 40         free(p);
 41     }
 42
 43     static void *reallocate(void* p,size_t old_sz,size_t new_sz)
 44     {
 45         void* result = realloc(p,new_sz);//第一级配置器直接使用realloc()
 46         //当以下无法满足时,改用oom_realloc()
 47         if(0 == result)
 48             result = oom_realloc(p,new_sz);
 49         return result;
 50     }
 51
 52     //以下仿真C++的set_new_handler() 换句话说,你可以通过它
 53     //指定你自己的out_of_memory handler
 54     static void (* set_new_handler(void (*f)() ) ) ()
 55     {           //指针函数      //函数指针
 56
 57         void (*old)() = __malloc_alloc_oom_handler;
 58         __malloc_alloc_oom_handler = f;
 59         return (old);
 60     }
 61 };
 62
 63     //malloc_alloc out_of_memory handling
 64     //初值为0 有待客户端设定
 65 template<int inst>
 66 void (* __malloc_alloc_template<inst>::__malloc_alloc_oom_handler) () = 0;
 67
 68 template<int inst>
 69 void* __malloc_alloc_template<inst>::oom_malloc(size_t n)
 70 {
 71     void (*my_malloc_handler)();
 72     void* result;
 73
 74     for(;;)
 75     {   //不断尝试释放,配置,再释放,再配置
 76         my_malloc_handler = __malloc_alloc_oom_handler;
 77         if(0 == my_malloc_handler)
 78         {
 79             __THROW_BAD_ALLOC;
 80         }
 81         (*my_malloc_handler)(); //调用处理例程,企图释放内存
 82         result = malloc(n);     //再次尝试配置内存
 83         if(result)
 84             return result;
 85     }
 86 }
 87
 88 template<int inst>
 89 void* __malloc_alloc_template<inst>::oom_realloc(void* p,size_t n)
 90 {
 91     void (*my_malloc_handler)();
 92     void* result;
 93
 94     for(;;)
 95     {   //不断尝试释放 配置 再释放 再配置
 96         my_malloc_handler = __malloc_alloc_oom_handler;
 97         if(0 == my_malloc_handler)
 98         {
 99             __THROW_BAD_ALLOC;
100         }
101         (*my_malloc_handler)();     //调用处理例程 企图释放内存
102         result = realloc(p,n);      //再次尝试释放内存
103         if(result)
104             return result;
105     }
106 }
107
108 //注意 一下直接将参数inst指定为0
109 typedef __malloc_alloc_template<0> malloc_alloc;
110
111 /*******************************************/
112 //二级空间配置器
113
114 enum {__ALIGN = 8};             //小型区块的上调边界
115 enum {__MAX_BYTES = 128};       //小型区块的上限
116 enum {__NFREELISTS = __MAX_BYTES / __ALIGN};    //free_lists个数
117
118 //以下是第二级配置器
119 //注意 无"template型别参数",且第二个参数完全没有排上用场
120 //第一参数用于多线程环境下
121
122 template<bool threads,int inst>
123 class __default_alloc_template
124 {
125 private:
126     //ROUND_UP()将bytes上调至8的倍数
127     static size_t ROUND_UP(size_t bytes)
128     {   //__ALIGN必须是2的正次方
129         return ( ( (bytes) + __ALIGN-1) & ~(__ALIGN - 1) );
130     }
131 private:
132     union obj
133     {
134         union obj* free_lists_link;
135         char client_data[1];    //The client sees this
136     };
137 private:
138     //16个free_lists
139     static obj* volatile free_list[__NFREELISTS];
140     static size_t FREELIST_INDEX(size_t bytes)
141     //以下函数根据区块大小,决定使用第n号free_list.n从0开始
142     {
143         return ( ( (bytes) + __ALIGN-1)/__ALIGN - 1);
144     }
145     //返回一个大小为n的对象,并可能加入大小为n的其他区块到free_list
146     static void* refill(size_t n);
147
148     //配置一块大空间,可容纳nobjs个大小为"size"的区块
149     //如果配置nobjs个区块有所不便,nobjs可能会降低
150     static char* chunk_alloc(size_t size,int &nobjs);
151
152     //Chunk allocation state
153     static char *start_free;        //内存池起始位置
154     static char *end_free;          //内存池结束位置
155     static size_t heap_size;
156
157 public:
158     static void* allocate(size_t n);
159
160     static void deallocate(void *p,size_t n);
161
162     static void* reallocate(void *p,size_t old_sz,size_t new_sz);
163 };
164
165 template<bool threads,int inst>
166 char *__default_alloc_template<threads,inst>::start_free = 0;
167
168 template<bool threads,int inst>
169 char *__default_alloc_template<threads,inst>::end_free = 0;
170
171 template<bool threads,int inst>
172 size_t __default_alloc_template<threads,inst>::heap_size = 0;
173
174 template<bool threads,int inst>
175 typename __default_alloc_template<threads,inst>::obj* volatile __default_alloc_template<threads,inst>::free_list[__NFREELISTS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
176
177 template<bool threads,int inst>
178 //n必须大于0
179 void* __default_alloc_template<threads,inst>::allocate(size_t n)
180 {   //volatile关键字所定义的变量 随时有可能得到改变
181     obj* volatile *my_free_list;
182     obj* result;
183     //大于128就调用1级空间配置器
184     if(n > (size_t) __MAX_BYTES)
185         return (malloc_alloc::allocate(n));
186
187     //寻找16个free list中适当的一个
188     my_free_list = free_list + FREELIST_INDEX(n);
189     result = *my_free_list;
190     if(result == 0)
191     {   //没找到可用的free list 准备重新填充free list
192         void* r = refill(ROUND_UP(n));
193         return r;
194     }
195     //调整free list
196     *my_free_list = result->free_lists_link;
197     return result;
198 }
199
200 template<bool threads,int inst>
201 void __default_alloc_template<threads,inst>::deallocate(void *p,size_t n)
202 {
203     obj *q = (obj*)p;
204     obj * volatile *my_free_list;
205
206     //大于128就调用一级配置器
207     if(n > (size_t) __MAX_BYTES)
208     {
209         malloc_alloc::deallocate(p,n);
210         return;
211     }
212
213     //寻找对应的free list
214     my_free_list = free_list + FREELIST_INDEX(n);
215     //调整free list 回收区块
216     q->free_lists_link = *my_free_list;
217     *my_free_list = q;
218 }
219
220 //返回一个大小为n的对象,并且有时候会为适当的free list增加节点
221 //假设n已经适当上调至8的倍数
222 template<bool threads,int inst>
223 void* __default_alloc_template<threads,inst>::refill(size_t n)
224 {
225     int nobjs = 20;
226     //调用chunk_alloc(),尝试取得nobjs个区块作为free list的新节点
227     //注意参数nobjs是pass by reference
228     char* chunk = chunk_alloc(n,nobjs);
229     obj* volatile *my_free_list;
230     obj* result;
231     obj* current_obj,*next_obj;
232     int i;
233
234     //如果只获得一个区块,这个区块就分配给调用者用,free list无新节点
235     if(1 == nobjs)
236         return chunk;
237     my_free_list = free_list + FREELIST_INDEX(n);
238
239     //以下在chunk空间内建立free list
240     result = (obj*)chunk;   //这一块准备返回给客端
241     //以下导引free list指向新配置的空间(取自内存池)
242     *my_free_list = next_obj = (obj*)(chunk + n);
243     //以下将free_list的各节点串接起来
244     for(i = 1; ; ++i)
245     {
246         current_obj = next_obj;
247         next_obj = (obj*)((char*)next_obj + n);
248         if(nobjs - 1 == i)
249         {
250             current_obj->free_lists_link = 0;
251             break;
252         }
253         else
254         {
255             current_obj->free_lists_link = next_obj;
256         }
257     }
258     return result;
259 }
260
261 //假设size已经适当上调至8的倍数
262 //注意参数nobjs是pass by reference
263 template<bool threads,int inst>
264 char* __default_alloc_template<threads,inst>::chunk_alloc(size_t size,int& nobjs)
265 {
266     char* result;
267     size_t total_bytes = size * nobjs;
268     size_t bytes_left = end_free - start_free;  //内存池剩余空间
269
270     if(bytes_left >= total_bytes)
271     {//内存池剩余空间完全满足需求量
272         result = start_free;
273         start_free += total_bytes;
274         return result;
275     }
276     else if(bytes_left >= size)
277     {//内存池剩余空间不能完全满足需求量,但足够供应一个以上的区块
278         nobjs = bytes_left/size;
279         total_bytes = size * nobjs;
280         result = start_free;
281         start_free += total_bytes;
282         return result;
283     }
284     else
285     {//内存池连一个区块的大小都无法提供
286         size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
287      //一下试着让内存池中的残余零头还有利用价值
288      if(bytes_left > 0)
289      {//内存池内还有一些零头 先配给适当的free_list
290       //首先寻找适当的free_list
291       obj* volatile *my_free_list = free_list + FREELIST_INDEX(bytes_left);
292       //调整free_list,将内存池中的残余空间编入
293       ((obj*)start_free)->free_lists_link = *my_free_list;
294       *my_free_list = (obj*)start_free;
295      }
296
297      //配置heap空间,用来补充内存池
298      start_free = (char*)malloc(bytes_to_get);
299      if(0 == start_free)
300      {//heap空间不足,mallo()失败
301         int i;
302         obj *volatile *my_free_list,*p;
303         //试着检查我们手上拥有的东西,这不会造成伤害,我们不打算尝试配置
304         //较小的区块,因为那在多进程(multi-process)机器上容易导致灾害
305         //以下搜寻适当的free_list
306         //所谓适当是指"尚有未用区块 且区块够大"之free_list
307         for(i = size; i <= __MAX_BYTES; i += __ALIGN)
308         {
309             my_free_list = free_list + FREELIST_INDEX(i);
310             p = *my_free_list;
311             if(0 != p)
312             {//free_list内尚有未用区块
313             //调整free_list以释放未用区块
314                 *my_free_list = p->free_lists_link;
315                 start_free = (char*)p;
316                 end_free = start_free + i;
317                 //递归调用自己,为了修正nobjs
318                 return chunk_alloc(size,nobjs);
319                 //注意 任何残余零头终将被编入适当的free_list中备用
320             }
321         }
322         end_free = 0;   //如果出现意外
323         //调用第一级配置器,看看out_of_memory机制是否能够尽力
324         start_free = (char*)malloc_alloc::allocate(bytes_to_get);
325         //这会导致抛出异常(exception),或内存不足的情况获得改善
326         heap_size += bytes_to_get;
327         end_free = start_free + bytes_to_get;
328         //递归调用自己,为了修正nobjs
329         return chunk_alloc(size,nobjs);
330      }
331     }
332
333 }
TestMain.cpp
  1 #include"./stl_alloc.h"
  2
  3 //采用一级空间配置器的做法
  4 /*
  5 void Out_of_Memory()
  6 {
  7     cout<<"My Out Of Memory"<<endl;
  8     exit(1);
  9 }
 10
 11 int main()
 12 {
 13     __malloc_alloc_template<0>::set_new_handler(Out_of_Memory);
 14     int *p = (int*)__malloc_alloc_template<0>::allocate(sizeof(int)*536870911);
 15     __malloc_alloc_template<0>::deallocate(p,sizeof(int));
 16     return 0;
 17 }
 18 */
 19
 20 int main()
 21 {
 22     int *p = (int*)__default_alloc_template<0,0>::allocate(sizeof(int));
 23     int *p1 = (int*)__default_alloc_template<0,0>::allocate(sizeof(int));
 24     return 0;
 25 }


可以自己单步调试进去看看运行情况。最好结合侯捷先生的《STL源码剖析》一起阅读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值