首页 > 代码库 > STL源码剖析(空间配置器)
STL源码剖析(空间配置器)
前言
在STL中,容器的定义中都带一个模板参数,如vector
template <class T, class Alloc = alloc> class vector {...}
其中第二个参数就是该容器使用的空间配置器,其中缺省使用STL已经实现的空间配置器(alloc),
该配置器使用malloc/free等为vector分配内存。
缺省的空间配置器
alloc定义了两级的空间配置器,第一级是对malloc/free简单的封装。
而为了解决内存碎片的问题,跟进行内存管理,alloc实现的第二级的空间配置器。
第二级空间配置器在分配大块内存(大于128bytes)时,会直接调用第一级空间配置器,
而分配小于128bytes的内存时,则使用内存池跟free_list进行内存分配/管理。
第一级空间配置器
基本实现如下(跟SGI STL可能有点出入,主要是提取核心的内容)
1 class base_alloc { 2 public: 3 // 只是对malloc/free的简单封装 4 static void* allocate(size_t n) 5 { 6 void* res = malloc(n); 7 if (0 == res) res = oom_malloc(n); 8 return res; 9 } 10 static void* reallocate(void* p, size_t new_sz) 11 { 12 void* res = realloc(p, new_sz); 13 if (0 == res) res = oom_realloc(p, new_sz); 14 return res; 15 } 16 static void deallocate(void* p) 17 { 18 free(p); 19 } 20 // 用来设置内存不足时的处理函数 该函数参数跟返回值都是一个函数指针 21 // 一般会抛出异常/尝试回收内存 22 static void(*set_handler(void(*f)()))() 23 { 24 void(*old)() = _oom_handler; 25 _oom_handler = f; 26 return old; 27 } 28 private: 29 // 用来处理内存不足的情况 30 static void* oom_malloc(size_t n) 31 { 32 void(*my_handler)(); 33 void* res; 34 35 for (;;) 36 { 37 my_handler = _oom_handler; 38 if (0 == my_handler) { return NULL; } 39 (*my_handler)(); 40 if (res = malloc(n)) return res; 41 } 42 } 43 // 用来处理内存不足的情况 44 static void* oom_realloc(void* p, size_t n) 45 { 46 void(*my_handler)(); 47 void* res; 48 49 for (;;) 50 { 51 my_handler = _oom_handler; 52 if (0 == my_handler) { return NULL; } 53 (*my_handler)(); 54 if (res = reallocate(p, n)) return res; 55 } 56 } 57 // 由用户设置,在内存不足的时候进行处理,由上面两个函数调用 58 static void(*_oom_handler)(); 59 }; 60 61 // 处理函数默认为0 62 void(*base_alloc::_oom_handler)() = 0;
它可以设定一个处理内存不足的时候的处理函数(跟set_new_handler类似)。
第二级空间配置器
该配置器维护一个free_list,这是一个指针数组。
在分配内存的时候,补足8bytes的倍数,free_list数组中每个指针分别管理分配大小为8、16、24、32...bytes的内存。
下图表示从二级空间配置器中分配内存时是如何维护free_list的(建议参考下面源码阅读)。
开始所有指针都为0,没有可分配的区块时(就是free_list[i]==0)会从内存池中分配内存(默认分配20个区块)插入到free_list[i]中。
然后改变free_list[i]的指向,指向下一个区块(free_list_link指向下一个区块,如果没有则为0)。
下图表示二级空间配置器回收内存时是如何维护free_list结构的。
回收的时候只是将区块插入到free_list[i]的开头,这块内存用于下次分配的时候使用。
该配置器实现如下(同样提取核心的部分):
1 enum { _ALIGN = 8 }; // 对齐 2 enum { _MAX_BYTES = 128 }; // 区块大小上限 3 enum { _NFREELISTS = _MAX_BYTES / _ALIGN }; // free-list个数 4 5 class default_alloc { 6 private: 7 // 将bytes上调到8的倍数 8 static size_t ROUND_UP(size_t bytes) 9 { 10 return (bytes + _ALIGN - 1) & ~(_ALIGN - 1); 11 } 12 private: 13 union obj { 14 union obj* free_list_link; 15 char client_data[1]; 16 }; 17 private: 18 // 16个free-lists 各自管理分别为8,16,24...的小额区块 19 static obj* free_list[_NFREELISTS]; 20 // 根据区块大小,决定使用第n号free-list 21 static size_t FREELIST_INDEX(size_t bytes) 22 { 23 return (bytes + _ALIGN - 1) / _ALIGN - 1; 24 } 25 // 分配内存,返回一个大小为n的区块,可能将大小为n的其他区块加入到free_list 26 static void* refill(size_t n) 27 { 28 // 默认分配20个区块 29 int nobjs = 20; 30 char* chunk = chunk_alloc(n, nobjs); 31 obj** my_free_list; 32 obj* result, *current_obj, *next_obj; 33 34 // 如果只分配了一个区块,直接返回 35 if (1 == nobjs) return chunk; 36 // 否则将其他区块插入到free list 37 my_free_list = free_list + FREELIST_INDEX(n); 38 result = (obj*)chunk; 39 // 第一个区块返回 后面的区块插入到free list 40 *my_free_list = next_obj = (obj*)(chunk + n); 41 for (int i = 1;; ++i) 42 { 43 current_obj = next_obj; 44 next_obj = (obj*)((char*)next_obj + n); 45 // 最后一个next的free_list_link为0 46 if (nobjs - 1 == i) 47 { 48 current_obj->free_list_link = 0; 49 break; 50 } 51 current_obj->free_list_link = next_obj; 52 } 53 return result; 54 } 55 // 分配内存 56 // 在内存池容量足够时,只调整start_free跟end_free指针 57 // 在内存池容量不足时,调用malloc分配内存(2 * size * nobjs + ROUND_UP(heap_size >> 4),每次调整heap_size += 本次分配内存的大小) 58 static char* chunk_alloc(size_t size, int& nobjs); 59 60 static char* start_free; //内存池的起始位置 61 static char* end_free; //内存池的结束位置 62 static size_t heap_size; //分配内存时的附加量 63 public: 64 static void* allocate(size_t n) 65 { 66 obj** my_free_list; 67 obj* result; 68 // 大于128就调用第一级空间配置器 69 if (n > (size_t)_MAX_BYTES) 70 return base_alloc::allocate(n); 71 72 // 寻找适当的free-list 73 my_free_list = free_list + FREELIST_INDEX(n); 74 result = *my_free_list; 75 // 没有可用的free list 76 if (result == 0) 77 return refill(ROUND_UP(n)); 78 79 // 调整free list 移除free list 80 *my_free_list = result->free_list_link; 81 return result; 82 } 83 static void deallocate(void* p, size_t n) 84 { 85 obj* q = (obj*)p; 86 obj** my_free_list; 87 88 // 大于128就调用第一级空间配置器 89 if (n > (size_t)_MAX_BYTES) 90 { 91 base_alloc::deallocate(p); 92 return; 93 } 94 95 // 寻找对应的free list 96 my_free_list = free_list + FREELIST_INDEX(n); 97 // 调整free list 回收区块(将区块插入到my_free_list) 98 q->free_list_link = *my_free_list; 99 *my_free_list = q; 100 } 101 static void reallocate(void* p, size_t old_sz, size_t new_sz); 102 }; 103 104 char* default_alloc::start_free = 0; 105 char* default_alloc::end_free = 0; 106 size_t default_alloc::heap_size = 0; 107 default_alloc::obj* default_alloc::free_list[_NFREELISTS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
STL源码剖析(空间配置器)