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源码剖析》一起阅读。