二级配置器
第一级是直接调用malloc分配空间, 调用free释放空间, 第二级三就是建立一个内存池, 小于128字节的申请都直接在内存池申请, 不直接调用malloc和free. 本节分析第二级空间配置器, STL将第二级配置器设置为默认的配置器, 所以只要一次申请的空间不超过128字节就默认在内存池中申请空间, 超过才会调用第一级配置器.
首先先来介绍3个常量
__ALIGN: 以8字节进行对齐
__MAX_BYTES : 二级分配器最大分配的内存大小
__NFREELISTS : 128字节能分配的的链表个数, 并且从每个链表保存的内存大小都是8的倍数, 而且都比前一个大8字节, 也就是分别是8, 16, 32…128字节
1 2 3 4
| enum {__ALIGN = 8}; enum {__MAX_BYTES = 128}; enum {__NFREELISTS = __MAX_BYTES/__ALIGN};
|
再介绍一个宏操作, 这是进行对齐操作, 将不满8的倍数的填充成8的倍数.
1 2 3 4
| static size_t FREELIST_INDEX(size_t bytes) \ {\ return (((bytes) + ALIGN-1) / __ALIGN - 1);\ }
|
从allocate先切入分析
- 先判断申请的字节大小是不是大于128字节, 是, 则交给第一级配置器来处理. 否, 继续往下执行
- 找到分配的地址对齐后分配的是第几个大小的链表.
- 获得该链表指向的首地址, 如果链表没有多余的内存, 就先填充链表.
- 返回链表的首地址, 和一块能容纳一个对象的内存, 并更新链表的首地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| static void * allocate(size_t n) { obj * __VOLATILE * my_free_list; obj * __RESTRICT result; if (n > (size_t) __MAX_BYTES) { return(malloc_alloc::allocate(n)); } my_free_list = free_list + FREELIST_INDEX(n); result = *my_free_list; if (result == 0) { void *r = refill(ROUND_UP(n)); return r; } *my_free_list = result -> free_list_link; return (result); };
|
refill内存填充:
- 向内存池申请空间的起始地址
- 如果只申请到一个对象的大小, 就直接返回一个内存的大小, 如果有更多的内存, 就继续执行
- 从第二个块内存开始, 把从内存池里面分配的内存用链表给串起来, 并返回一个块内存的地址给用户
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
| template <bool threads, int inst> void* __default_alloc_template<threads, inst>::refill(size_t n) { int nobjs = 20; char * chunk = chunk_alloc(n, nobjs); obj * __VOLATILE * my_free_list; obj * result; obj * current_obj, * next_obj; int i;
if (1 == nobjs) return(chunk); my_free_list = free_list + FREELIST_INDEX(n);
result = (obj *)chunk; *my_free_list = next_obj = (obj *)(chunk + n); for (i = 1; ; i++) { current_obj = next_obj; next_obj = (obj *)((char *)next_obj + n); if (nobjs - 1 == i) { current_obj -> free_list_link = 0; break; } else { current_obj -> free_list_link = next_obj; } } return(result); }
|
统一的接口
定义符合STL规格的配置器接口, 不管是一级配置器还是二级配置器都是使用这个接口进行分配的
1 2 3 4 5 6 7 8 9 10 11 12 13
| template<class T, class Alloc> class simple_alloc { public: static T *allocate(size_t n) { return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); } static T *allocate(void) { return (T*) Alloc::allocate(sizeof (T)); } static void deallocate(T *p, size_t n) { if (0 != n) Alloc::deallocate(p, n * sizeof (T)); } static void deallocate(T *p) { Alloc::deallocate(p, sizeof (T)); } };
|
用链表来保存不同字节大小的内存块, 就很容易的进行维护, 而且每次的内存分配都直接可以从链表或者内存池中获得, 提升了我们申请内存的效率, 毕竟每次调用malloc和free效率是很低的, 特别是很小内存的时候.
STL默认的就是第二级配置器, 它会自动判断我们使用哪一个配置器.