STL为了各个容器高效的管理空间,设计一块高效的内存管理机制,避免了频繁向系统申请小块内存块造成内存碎片、影响程序运行效率,和直接使用malloc和new申请额外空间浪费、申请空间失败的问题,还考虑了线程安全问题。虽然内核中已经有了一个类似的slab分配器来管理小块内存但是内核是针对操作系统中所有程序的,他申请和释放内存的消耗会非常大,STL需要的全部都是小块内存,并且需求比较集中,如果自己设计一个效率更高,还能顺带解决内存碎片的问题
SGI-STL的空间配置器分为分为两级结构,一级空间配置器处理大块内存(大于128字节),二级空间配置器处理小块内存
一级空间配置器对malloc和free进行了封装,同时加上了对内存不足的处理方法每当通过malloc或者realloc申请空间时,如果出现了内存不足的情况,就会循环调用oom来进行处理,然后继续判断是否申请成功,失败则继续尝试。若用户不自己设定oom则内存不足时抛出bad_alloc异常
二级空间配置器专门负责处理小于128字节的小块内存。采用了内存池的技术来提高申请空间的速度以及减少额外空间的浪费,采用哈希桶的方式来提高用户获取空间的速度与高效管理,避免外碎片问题(但还是存在内碎片问题)
首先申请一大块内存,并且用类似哈希桶的结构来维护这个内存池,每一个桶中装载一个free-list单链表。总共维护16个free-lists,各自管理大小以8字节为倍数,依次是8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128字节,SGI-STL将用户申请的内存块向上对齐到了8的整数倍
当用户申请的空间小于128字节则在桶中寻找可利用的内存
- 如果有则取出分给用户
- 如果没有则需要调用refill向内存池申请空间,refill中调用chunk_alloc一次申请对应字节的20块内存
2.1 若内存池内存足够,则将一个分配给用户,其余内存挂到哈希桶中
2.2 若内存池中内存小于20块大于1块,则同样将一个分配给用户,其余内存挂到哈希桶中
2.3 若内存不足1块,则把内存池中剩余的空间挂到对应哈希桶中,然后调用malloc向系统申请
2.3.1 若申请成功则放入内存池,重复调用chunk_alloc
2.3.2 若申请失败则从哈希桶中找更大的内存块
2.3.2.1 如果有则从哈希桶中取出给用户
2.3.2.2 如果没有则调用一级空间配置器,成功则放入内存重复调用chunk_alloc,失败抛异常
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
| #ifdef USE_MALLOC
typedef malloc_alloc_template malloc_alloc; typedef malloc_alloc alloc; #else
typedef default_alloc_tmplate alloc; #endif
class malloc_alloc_template { private: static void* oom_malloc(size_t); static void(*malloc_alloc_oom_handler)(); public: static void* allocate(size_t n) { void* result = malloc(n); if (result == 0) { result = oom_malloc(n); } return result; }
static void deallocate(void* p) { free(p); }
static void(*set_malloc_handler(void(*f)()))() { void(*old)() = malloc_alloc_oom_handler; malloc_alloc_oom_handler = f; return old; } };
void(*malloc_alloc_template::malloc_alloc_oom_handler)() = nullptr;
void* malloc_alloc_template::oom_malloc(size_t n) { void(*my_malloc_handler)() = nullptr; void* result = nullptr; while (1) { my_malloc_handler = malloc_alloc_oom_handler; if (my_malloc_handler == nullptr) { throw std::bad_alloc(); } (*my_malloc_handler)(); result = malloc(n); if (result) { return result; } } }
enum { ALIGN = 8 }; enum { MAX_BYTES = 128 }; enum { NFREELISTS = MAX_BYTES / ALIGN };
class default_alloc_tmplate { private: static size_t ROUND_UP(size_t bytes) { return (bytes + ALIGN - 1) & ~(ALIGN - 1); }
union obj { union obj* free_list_link; char client_data[1]; };
static obj* free_list[NFREELISTS];
static size_t FREELIST_INDEX(size_t bytes) { return (bytes + ALIGN - 1) / ALIGN - 1; }
static void* refill(size_t n);
static char* chunk_alloc(size_t size, int& nobjs);
static char* start_free; static char* end_free; static size_t heap_size; public: static void* allocate(size_t n); static void deallocate(void* p, size_t n); };
char* default_alloc_tmplate::start_free = nullptr; char* default_alloc_tmplate::end_free = nullptr; size_t default_alloc_tmplate::heap_size = 0; default_alloc_tmplate::obj* default_alloc_tmplate::free_list[NFREELISTS] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
void* default_alloc_tmplate::refill(size_t n) { int nobjs = 20; char* chunk = chunk_alloc(n, nobjs); if (nobjs == 1) { return chunk; } obj* result = (obj*)chunk; obj** my_free_list = free_list + FREELIST_INDEX(n); obj* next_obj = *my_free_list = (obj*)(chunk + n); for (int i = 1; i < nobjs ; i++) { obj* current_obj = next_obj; next_obj = (obj*)((char*)next_obj + n); if (i == nobjs - 1) { current_obj->free_list_link = nullptr; } else { current_obj->free_list_link = next_obj; } } return result; }
char* default_alloc_tmplate::chunk_alloc(size_t size, int& nobjs) { char* result; size_t total_bytes = size * nobjs; size_t bytes_left = end_free - start_free;
if (bytes_left >= total_bytes) { result = start_free; start_free += total_bytes; return result; } else if (bytes_left >= size) { nobjs = bytes_left / size; total_bytes = size * nobjs; result = start_free; start_free += total_bytes; return result; } else { size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4); if (bytes_left > 0) { obj** my_free_list = free_list + FREELIST_INDEX(bytes_left); ((obj*)start_free)->free_list_link = *my_free_list; *my_free_list = (obj*)start_free; }
start_free = (char*)malloc(bytes_to_get); if (start_free == 0) { for (int i = size; i <= MAX_BYTES; i += ALIGN) { obj** my_free_list = free_list + FREELIST_INDEX(i); obj* p = *my_free_list; if (p) { *my_free_list = p->free_list_link; start_free = (char*)p; end_free = start_free + i; return chunk_alloc(size, nobjs); } } end_free = nullptr; start_free = (char*)malloc_alloc_template::allocate(bytes_to_get); } heap_size += bytes_to_get; end_free = start_free + bytes_to_get; return chunk_alloc(size, nobjs); } }
void* default_alloc_tmplate::allocate(size_t n) { if (n > (size_t)MAX_BYTES) { return malloc_alloc_template::allocate(n); } obj** my_free_list = free_list + FREELIST_INDEX(n); obj* result = *my_free_list; if (result == 0) { void* r = refill(ROUND_UP(n)); return r; } *my_free_list = result->free_list_link; return result; }
void default_alloc_tmplate::deallocate(void* p, size_t n) { if (n > (size_t)MAX_BYTES) { malloc_alloc_template::deallocate(p); return; } obj** my_free_list = free_list + FREELIST_INDEX(n); obj* q = (obj*)p; q->free_list_link = *my_free_list; *my_free_list = q; }
template <class T, class Alloc> class simple_alloc { public: static T* allocate(size_t n) { return n ? (T*)Alloc::allocate(n * sizeof(T)) : nullptr; }
static T* allocate(void) { return (T*)Alloc::allocate(sizeof(T)); }
static void deallocate(T *p, size_t n) { if (n) { Alloc::deallocate(p, n * sizeof(T)); } }
static void deallocate(T *p) { Alloc::deallocate(p, sizeof(T)); } };
template <class T> void destroy(T* pointer) { pointer->~T(); }
template <class T1, class T2> void construct(T1* p, const T2& value) { new(p) T1(value); }
|