上一节只分析了第二级配置器是由多个链表来存放相同内存大小, 当没有空间的时候就向内存池索取就好了, 却没有具体分析内存池是怎么保存空间的, 是否是内存池真的有用不完的内存, 本节咱们就具体来分析一下code
static data template的初始化对象
template <bool threads, int inst> char *__default_alloc_template<threads, inst>::start_free = 0; // 内存池的首地址 template <bool threads, int inst> char *__default_alloc_template<threads, inst>::end_free = 0; // 内存池的结束地址 template <bool threads, int inst> size_t __default_alloc_template<threads, inst>::heap_size = 0; // 屡次调用内存池, 就会更多的是给链表分配内存, 这就是一个增量.
这里代码注释写的很清楚了, 我就提取出来分析一下吧递归
- 内存池的大小大于须要的空间, 直接返回起始地址(nobjs默认设置为20, 因此每次调用都会给链表额外的19个内存块)
- 内存池的内存不足以立刻分配那么多内存, 可是还能知足分配一个即以上的大小, 那就所有分配出去
- 若是一个对象的大小都已经提供不了了, 先将零碎的内存块给一个小内存的链表来保存, 而后就准备调用malloc申请40块+额外大小的内存块(额外内存块就由heap_size决定), 若是申请失败跳转到步骤4, 成功跳转到步骤6
- 充分利用更大内存的链表, 经过递归来调用他们的内存块
- 若是仍是没有内存块, 直接调用一级配置器来申请内存, 仍是失败就抛出异常, 成功申请就继续执行
- 从新修改内存起始地址和结束地址为当前申请的地址块, 从新调用chunk_alloc分配内存
// 内存池 template <bool threads, int inst> char* __default_alloc_template<threads, inst>::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 { // 若是一个对象的大小都已经提供不了了, 那就准备调用malloc申请两倍+额外大小的内存 size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4); // Try to make use of the left-over piece. // 内存池还剩下的零头内存分给给其余能利用的链表, 也就是毫不浪费一点. if (bytes_left > 0) { // 链表指向申请内存的地址 obj * __VOLATILE * 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 (0 == start_free) { int i; obj * __VOLATILE * my_free_list, *p; // 充分利用剩余链表的内存, 经过递归来申请 for (i = size; i <= __MAX_BYTES; i += __ALIGN) { my_free_list = free_list + FREELIST_INDEX(i); p = *my_free_list; if (0 != p) { *my_free_list = p -> free_list_link; start_free = (char *)p; end_free = start_free + i; return(chunk_alloc(size, nobjs)); } } // 若是一点内存都没有了的话, 就只有调用一级配置器来申请内存了, 而且用户没有设置处理例程就抛出异常 end_free = 0; // In case of exception. start_free = (char *)malloc_alloc::allocate(bytes_to_get); } // 申请内存成功后从新修改内存起始地址和结束地址, 从新调用chunk_alloc分配内存 heap_size += bytes_to_get; end_free = start_free + bytes_to_get; return(chunk_alloc(size, nobjs)); } }
内存池的存在就是为了能快速的提供咱们作须要的内存而且保存多余的空间, 让STL分配空间再也不每次都进行malloc和free的操做, 效率又颇有保障. 有时用户申请的块更小, 咱们也能充分的利用起来. 惟一可能不足的是咱们每次只申请char
个大小, 可是内存池得到的确是8字节的大小.内存