重新填充Refill_Freelist
在allocate()分配內存發現freelist沒有可用內存時,需要調用refill函數重新填充freelist,這些新的空間將從內存池中獲得,一次分配20個節點或區塊,當然如果內存池內存不足很有可能區塊會小於20個。
//假設n已經上調至8的倍數
template<bool threads,int inst>
void* _default_alloc_template<threads,inst>::refill(size_t n){
int nobjs = 20;
char* chunk = chunk_alloc(n,nobjs);//nobjs傳引用
obj* volatile *my_free_list;
if(1==nobjs) retrun(chunk);
my_free_list = free_list + FREELIST_INDEX(n);
obj* result;
obj* current_obj, * next_obj;
result = (obj*) chunk;//第一塊給客端
*my_free_list = next_obj = (obj*)(chunk+n);//剩下的串起來
for(int i=1;;i++){
currnt_obj = next_obj;
next_obj = (obj*)((char*)next_obj+n);
if(nobj-1 == i){
currnt_obj->free_list_link = 0;
break;
}else{
currnt_obj->free_list_link = next_obj;
}
}
return (result);
}
內存池取空間的CHUNK_ALLOC>
chunk_alloc函數是以end_free-start_free來判斷內存池是否還有可用內存,如果有則撥款20個nobjs,如若不足20但仍有超過一個需求大小的內存塊,則把這些空間全部撥出去。這時候nobjs就變成了實際得到內存塊。如果一個都無法配置,那麼內存池將會調用malloc從heap上獲取內存,內存量爲需求量的2倍加上一個和配置次數成比例的附加量。舉個例子:chunk_alloc(32,20),這是第一次內存池沒有內存,便向heap申請32*40的內存,其中1個給客端,19個給free_list維護,剩下的留給自己。這時候chunk_alloc(64,20),內存池還有32*20/64,也就是10個可用內存塊,1個給客端,9個給相應的freelist維護。自己空空如也了,下次在申請就是2倍加附加量了。萬一山窮水盡了,heap也沒了,那麼就調用第一級配置器,雖然他也是malloc,但是他有out_of_memory機制可以釋放其他內存拿來用,失敗了大不了喊一句bad_alloc;
//內存池chunk_alloc的實現,模板省略了
char* chunk_alloc(size_t size,int& nobjs){
char* result;
size_t total_bytes = size * nobjs;
size_t left_bytes = end_free - start_free;
if(left_bytes >= total_bytes){
//內存池完全夠用
result = start_free;
start_free += total_bytes;
return (result);
}else if(left_bytes >= size){
nobjs = left_bytes / size ;
result = start_free;
start_free += nobjs * size;
return (result);
}else{
//內存池一個區塊的大小都無法提供
size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size>>4);
if(left_bytes >0){
//內存池還有一點零頭,先配置給合適的free_list
obj* volatile* my_free_list = free_list + FREELIST_INDEX(left_bytes);
((obj*) start_free) -> free_list_link = *my_free_list;
*my_free_list = (obj*) start_free;
}
//在heap上配置空間,用來補充內存池
start_free = (char*)malloc(byte_to_get);
if(0==start_free){
//heap空間失敗,malloc()失敗
int i;
obj* volatile* my_free_list,*p;
//我們檢視手上的東西,找到適當大小的未用內存,適當即爲區塊要足夠大。
for(i = size;i<=_MAX_BYTES;i += _ALLIN){
my_free_list = free_list + FREELIST_INDEX(i);
p = my_free_list;
if(0 != p){
//還有貨,當然我們就拿1個區塊
*my_free_list = p -> free_list_link;
start_free = (char*)p;
end_free = start_free + i;
//內存找到了,則遞歸重新配置修正nobjs
return (chunk_alloc(size,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));
}
}
內存處理的基本工具
該函數會調用construct(&*(result + (i-first)),*i);如果你要實現一個容器的全區間構造函數,一般經過兩個步驟,第一個配置內存空間,第二個調用copy();
該函數會調用construct(&*i,*i);
這裏面value_type()判斷迭代器所指是否爲POD類型。POD爲plane old data,包含標量型或C 裏的結構體類型,對於POD類型必然擁有copy/assignment/dtor,因此我有更高效的初值填寫手法。否則採用最保險的做法。