源代码附在最后,并且会进行详细的注释(个人理解还有STL(侯捷)源码剖析的相关内容)
空间配置器:
1.空间配置器是对内存进行管理,内存的申请和释放
2.空间配置器分为两部分
<1>简单空间配置器,也叫一级配置器
<2>基于内存池的空间配置器,也叫二级空间配置器
一.SGI STL 第一级配置器
对malloc / free的简单封装,但是这个提供了异常处理机制。
在以及空间配置器中,有两点需要注意:
<1>.allocate()直接使用malloc();
<2>.模拟C++的set_new_handler()以处理内存不足的状况
对于第一级空间配置器,我会在代码的地方进行详细的注释。
第一级空间配置器的allocate()和 realloc()都是在调用malloc()和realloc()失败之后,然后调用oom_malloc()和oom_realloc()。后面两个都是内循环,不断的调用“内存不足处理例程”,期望在某次调用之后,得到足够的内存而完成任务。
注:C++的set_new_handler()机制是在要求系统在内存分配需求无法满足时,调用一个你所指定的函数,也就是说一旦::operator new 无法完成任务时,在丢出std::bab_alloc异常状态之前,会先调用由客户端指定的处理例程来解决内存不足。
二.SGI STL第二级空间配置器
第二级配置器的作用:因为大量的小区块会造成内存碎片,在配置时有额外的负担,应为系统要靠这多出来的空间管理内存。
这个有三点需要注意:
1.一共十六个自由链表(free lists)管理十六个小型区块的配置能力。内存池(memory pool) 以malloc()配置而得,如果内存不足,转调第一级空间配置器。
2.如果需求区块大于128byes,就转调第一级空间配置器,当区块小于128 byes时,则以内存池管理。
3.配置器除了负责配置还要负责回收。
在每一次配置一大块内存时,并维护自由链表(free-list),如果下次有相同大小的内存需求,就之间从free-list中拔取。在二级配置器中,把小额区块内存调为8的倍数,维护16个自由链表,大小分别为:8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128 beys。
这几张图很重要,可以解开很多疑惑
附源码:
1
2 #if 1
3 #include<iostream>
4 #include<new>
5 #include<malloc.h>
6 using namespace std;
7 //#define __THROW_BAD_ALLOC throw bad_alloc
8 #define __THROW_BAD_ALLOC cerr<<"Throw bad alloc, Out Of Memory."<<endl; exit(1)
9 #elif !defined (__THROW_BAD_ALLOC)
10 #include<iostream.h>
11 #define __THROW_BAD_ALLOC cerr<<"out of memory"<<endl; exit(1);
12 #endif
13
14 template<int inst> //非类型模板
15 class __malloc_alloc_template
16 {
17 private: // 下面三个函数处理内存不足的情况
18 static void* oom_malloc(size_t);
19 static void* oom_realloc(void *, size_t);
20 static void(* __malloc_alloc_oom_handler)();
21
22 public:
23 static void* allocate(size_t n)
24 {
25 void *result = malloc(n); //第一级配置器直间使用malloc()函数
26 if(0 == result) // result==NULL如果result为空的时候,改用oom_malloc()
27 result = oom_malloc(n);
28 return result; //返回指针
29 }
30 static void deallocate(void *p, size_t) //重新分配
31 {
32 free(p); //第一级空间适配器可以直接使用free()函数
33 }
34 static void* reallocate(void *p, size_t, size_t new_sz)
35 {
36 void *result = realloc(p, new_sz); //第一级配置器直间使用malloc()函数
37 if(0 == result)
38 oom_realloc(p,new_sz); 如果result为空的时候,改用oom_realloc()
39 return result;
40 }
41 public:
42 //set_new_handler(Out_Of_Memory); //set_new_handler()以处理内存不足的状况
//仿真C++的set_new_handler()
43 static void(*set_malloc_handler(void(*f)()))() //这个是函数,不是函数指针
44 {
45 void(*old)() = __malloc_alloc_oom_handler;
46 __malloc_alloc_oom_handler = f;
47 return old;
48 }
49 };
50
51 template<int inst>
52 void (*__malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;
53
54 template<int inst>
55 void* __malloc_alloc_template<inst>::oom_malloc(size_t n)
56 {
57 void *result;
58 void(* my_malloc_handler)();
59
60 for(;;) //不断的尝试释放、配置、再释放、再配置
61 {
62 my_malloc_handler = __malloc_alloc_oom_handler;
63 if(0 == my_malloc_handler)
64 {
65 __THROW_BAD_ALLOC;
66 }
67 (*my_malloc_handler)(); //调用处理例程,企图释放内存
68 result = malloc(n); //再次尝试配置内存
69 if(result)
70 return result;
71 }
72 }
73
74
75 template<int inst>
76 void* __malloc_alloc_template<inst>::oom_realloc(void *p, size_t n)
77 {
78 void(*my_malloc_handler)();
79 void *result;
80 for(;;)
81 {
82 my_malloc_handler = __malloc_alloc_oom_handler;
83 if(0 == my_malloc_handler)
84 {
85 __THROW_BAD_ALLOC;
86 }
87 (*my_malloc_handler)(); //调用处理例程,企图释放内存
88 result = realloc(p, n); //再次尝试配置内存
89 if(result)
90 return result;
91 }
92 }
93
94 typedef __malloc_alloc_template<0> malloc_alloc;
95 //第一级空间配置器结束
96 /////////////////////////////////////////////////////////////////////////////////////
97 //第二级空间配置器
98 enum {__ALIGN = 8}; //小型区块的上调边界
99 enum {__MAX_BYTES = 128}; //小型区块的上限
100 enum {__NFREELISTS = __MAX_BYTES / __ALIGN}; //自由链表的个数
101
102 template<bool threads, int inst>
103 class __default_alloc_template
104 {
105 public:
106 static void* allocate(size_t n);
107 static void deallocate(void *p, size_t n);
108 static void* reallocate(void *p, size_t, size_t new_sz);
109 private:
110 static size_t ROUND_UP(size_t bytes) //将byes上调到8的倍数
111 {
112 return (((bytes) + __ALIGN-1) & ~(__ALIGN-1));
113 }
114 private:
115 union obj
116 {
117 union obj * free_list_link; //构造自由链表的节点
118 char client_data[1];
119 };
120 private:
121 static obj* volatile free_list[__NFREELISTS];
122 static size_t FREELIST_INDEX(size_t bytes) //根据区块的大小,决定使用第n块free-list,n是从0开始
123 {
124 return ((bytes)+__ALIGN-1) / __ALIGN-1;
125 }
126 private:
127 static char *start_free; //内存池起始位置
128 static char *end_free; //内存池结束位置
129 static size_t heap_size;
130 static void *refill(size_t n); //返回大小为n的对象,并可能加入大小为n的其他区块到free-list
131 static char* chunk_alloc(size_t size, int &nobjs); //配置一大块空间,容纳nobjs个大小为“size”的区块
132 };
133
134
135 template<bool threads, int inst>
136 char* __default_alloc_template<threads, inst>::start_free = 0;
137 template<bool threads, int inst>
138 char* __default_alloc_template<threads, inst>::end_free = 0;
139 template<bool threads, int inst>
140 size_t __default_alloc_template<threads, inst>::heap_size = 0;
141 template<bool threads, int inst>
142 typename __default_alloc_template<threads, inst>::obj* volatile
143 __default_alloc_template<threads, inst>::free_list[__NFREELISTS] =
144 {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
145
146 template<bool threads, int inst>
147 void* __default_alloc_template<threads, inst>::allocate(size_t n)
148 {
149 obj * volatile *my_free_list; //二级指针
150 obj *result;
151
152 if(n > __MAX_BYTES) //大于128byes就调用第一配置器
153 {
154 return malloc_alloc::allocate(n);
155 }
156
157 my_free_list = free_list + FREELIST_INDEX(n); //寻找16个free-list中最合适的一个
158 result = *my_free_list;
159
160 if(result == 0) //如果没有找到就重新填充free-list
161 {
162 void *r = refill(ROUND_UP(n));
163 return r;
164 }
165
166 *my_free_list = result->free_list_link; //调整free-list
167 return result;
168 }
169
// 重新填充free-list
170 template<bool threads, int inst>
171 void* __default_alloc_template<threads, inst>::refill(size_t n)
172 {
173 int nobjs = 20; //经验值
174 char *chunk = chunk_alloc(n, nobjs); //调用chunk_alloc(),尝试取得nobjs个区块作为free-list的新节点
175 obj * volatile *my_free_list;
176
177 obj *result;
178 obj *current_obj, *next_obj;
179 int i;
180
181 if(1 == nobjs) //如果得到了一个区块,这个区块就分配给调用者
182 return chunk;
183
184 my_free_list = free_list + FREELIST_INDEX(n); //准备调整free-list,纳入新节点
185 result = (obj*)chunk; //返回给客户端
186 *my_free_list = next_obj = (obj*)(chunk+n);
187
188 for(i=1; ; ++i) //从第1个开始,第0个将返回给客户端
189 {
190 current_obj = next_obj;
191 next_obj = (obj*)((char*)next_obj+n);
192 if(nobjs - 1 == i)
193 {
194 current_obj->free_list_link = 0;
195 break;
196 }
197 else
198 {
199 current_obj->free_list_link = next_obj;
200 }
201 }
202 return result;
203 }
204
//内存池
205 template<bool threads, int inst>
206 char* __default_alloc_template<threads, inst>::chunk_alloc(size_t size, int &nobjs)
207 {
208 char *result;
209 size_t total_bytes = size * nobjs;
210 size_t bytes_left = end_free - start_free; //内存池剩余空间
211 if(bytes_left >= total_bytes) //内存池剩余空间完全满足需求量
212 {
213 result = start_free;
214 start_free += total_bytes;
215 return result;
216 }
217 else if(bytes_left >= size) //内存池剩余空间不能满足需求量,但可以提供一个以上的区块
218 {
219 nobjs = bytes_left / size;
220 total_bytes = size * nobjs;
221 result = start_free;
222 start_free += total_bytes;
223 return result;
224 }
225 else //内存池剩余空间连一个区块都无法满足
226 {
227 size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
228 if(bytes_left > 0) //试着让内存池中残余的零头还有利用价值
229 {
230 obj * volatile * my_free_list = free_list + FREELIST_INDEX(bytes_left);
231 ((obj*)start_free)->free_list_link = *my_free_list; //调整free-list,将内存池中残余的空间编入
232 *my_free_list = (obj *)start_free;
233 }
234
235 start_free = (char *)malloc(bytes_to_get); //配置heap空间,用来补充内存池
236 if(0 == start_free) //heap空间不足,malloc失败
237 {
238 int i;
239 obj * volatile *my_free_list, *p;
240 for(i=size; i<=__MAX_BYTES; i += __ALIGN)
241 {
242 my_free_list = free_list + FREELIST_INDEX(i);
243 p = *my_free_list;
244 if(0 != p) //free-list内有未用区块,调整free-list释放出未用区块
245 {
246 *my_free_list = p->free_list_link;
247 start_free = (char *)p;
248 end_free = start_free + i; //递归调用自己,为了修正nobjs
249 return chunk_alloc(size, nobjs); //将残余的编入free-list中备用
250 }
251 }
252 end_free = 0; //如果出现错误,调用第一级配置器
253 start_free = (char *)malloc_alloc::allocate(bytes_to_get);
254 }
255
256 heap_size += bytes_to_get;
257 end_free = start_free + bytes_to_get; //递归调用自己,为了修正nobjs
258 return chunk_alloc(size, nobjs);
259 }
260 }