第一级是直接调用malloc
分配空间, 调用free
释放空间, 第二级三就是创建一个内存池, 小于128字节的申请都直接在内存池申请, 不直接调用malloc
和free
. 本节分析第二级空间配置器, STL将第二级配置器设置为默认的配置器, 因此只要一次申请的空间不超过128字节就默认在内存池中申请空间, 超过才会调用第一级配置器.code
首先先来介绍3个常量.对象
__ALIGN
: 以8字节进行对齐__MAX_BYTES
: 二级分配器最大分配的内存大小__NFREELISTS
: 128字节能分配的的链表个数, 而且从每一个链表保存的内存大小都是8的倍数, 并且都比前一个大8字节, 也就是分别是8, 16, 32...128字节
// 二级配置器 enum {__ALIGN = 8}; // 设置对齐要求. 对齐为8字节, 没有8字节自动补齐 enum {__MAX_BYTES = 128}; // 第二级配置器的最大一次性申请大小, 大于128就直接调用第一级配置器 enum {__NFREELISTS = __MAX_BYTES/__ALIGN}; // 链表个数, 分别表明8, 16, 32....字节的链表
再介绍一个宏操做, 这是进行对齐操做, 将不满8的倍数的填充成8的倍数.接口
static size_t FREELIST_INDEX(size_t bytes) \ {\ return (((bytes) + ALIGN-1) / __ALIGN - 1);\ }
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
内存填充.内存
- 向内存池申请空间的起始地址
- 若是只申请到一个对象的大小, 就直接返回一个内存的大小, 若是有更多的内存, 就继续执行
- 从第二个块内存开始, 把从内存池里面分配的内存用链表给串起来, 并返回一个块内存的地址给用户
// 内存填充 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指向内存池返回的地址的下一个对齐后的地址 *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); }
- 释放的内存大于128字节直接调用一级配置器进行释放
- 将内存直接还给对应大小的链表就好了, 并不用直接释放内存, 以便后面分配内存的时候快速.
static void deallocate(void *p, size_t n) { obj *q = (obj *)p; obj * __VOLATILE * my_free_list; // 释放的内存大于128字节直接调用一级配置器进行释放 if (n > (size_t) __MAX_BYTES) { malloc_alloc::deallocate(p, n); return; } my_free_list = free_list + FREELIST_INDEX(n); q -> free_list_link = *my_free_list; *my_free_list = q; }
定义符合STL规格的配置器接口, 无论是一级配置器仍是二级配置器都是使用这个接口进行分配的class
// 定义符合STL规格的配置器接口, 无论是一级配置器仍是二级配置器都是使用这个接口进行分配的 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效率是很低的, 特别是很小内存的时候.thread
STL默认的就是第二级配置器, 它会自动判断咱们使用哪个配置器.效率