(4)重新填充空闲链表
在用 allocate配置空间时,如果空闲链表中没有可用数据块,就会调用refill来重新填充空间,新的空间取自内存池。缺省取20个数据块, 如果内存池空间不足,那么能取多少个节点就取多少个。 [cpp] view plain copy template <bool threads, int inst> void* refill(size_t n) { int nobjs = 20; char * chunk = chunk_alloc(n, nobjs);//从内存池里取出nobjs个大小为n的数据块,返回值nobjs为真实申请到的数据块个数,注意这里nobjs个大小为n的数据块所在的空间是连续的 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);//第0个数据块给调用者,地址访问即chunk~chunk + n - 1 for (i = 1; ; i++)//1~nobjs-1的数据块插入到空闲链表 { 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); } (5) 从内存池取空间 从内存池取空间给 空闲链表 用是chunk_alloc的工作: 首先根据end_free-start_free来判断内存池中的剩余空间是否足以调出nobjs个大小为size的数据块出去,如果内存连一个数据块的空间都无法供应,需要用malloc取堆中申请内存。 申请内存后,如果要拨出去20个大小为8字节的数据块。 假如山穷水尽,整个系统的堆空间都不够用了,malloc失败,那么chunk_alloc会从空闲链表中找是否有大的数据块,然后将该数据块的空间分给内存池(这个数据块会从链表中去除)。 [cpp] view plain copy template <bool threads, int inst> class __default_alloc_template { private: ...... static char *start_free;//内存池可用空间的起始位置,初始化为0 static char *end_free;//内存池可用空间的结束位置,初始化为0 static size_t heap_size;//内存池的总大小 public: //申请nobjs个大小为size的数据块,返回值为真实申请到的数据块个数,放在nobjs中 static char *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); } //剩余空间连一个数据块(大小为size)也无法提供 else { size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4); //内存池的剩余空间分给合适的空闲链表 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);//配置heap空间,用来补充内存池 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));//递归调用自己,为了修正nobjs } } end_free = 0; start_free = (char *)malloc_alloc::allocate(bytes_to_get);//如果连这个大的数据块都找不出来则调用第一级配置器 } //如果分配成功 heap_size += bytes_to_get;//内存池大小增加 end_free = start_free + bytes_to_get;//修改内存池可用空间的结束位置 return(chunk_alloc(size, nobjs));//递归调用自己,为了修正nobjs } } }; 参考: 《STL源码剖析》 http://www.cnblogs.com/sld666666/archive/2010/07/01/1769448.html