Allocate管理的是記憶體配置。
這裡主要考慮的是對於小額區塊的記憶體管理,如果小額區塊直接使用new操作符進行分配,第一會造成記憶體的片段;第二配置時會造成額外負擔;所謂額外負擔就是,每次索取一塊記憶體時,都必須向系統分配一塊空間,以記錄記憶體的大小。
所以這裡採用兩級配置器:
第一級配置器就是當要求的記憶體足夠大時,直接使用malloc和free配置和釋放記憶體;多大是足夠大?STL中定義當要求得記憶體大於128byte時,就是足夠大了。
//mymemory.h#ifndef _MYMEMORY#define _MYMEMORY#include<new>#define __THROW_BAD_ALLOC //throw bad_alloc//第一級配置器//此處inst完全沒有用到,根據Effective C++中記載,這樣可以產生不同的實體//使每個繼承此類的class,擁有實體互異的此類複件,主要是static成員變數template<int inst>class __malloc_alloc_template{private://以下函數處理記憶體不足的情況//oom: out of memorystatic void* oom_malloc(size_t);static void* oom_realloc(void*,size_t);static void (*__malloc_alloc_oom_handler)();public:/*interface: *allocate 配置記憶體 *deallocate 釋放記憶體 *reallocate重設記憶體 *///此處n代表nbytestatic void* allocate(size_t n){void* result =malloc(n);//第一級配置器直接使用malloc//當無法滿足要求時,改用oom_malloc(n)if(0==result) result=oom_malloc(n);return result;}static void deallocate(void* p,size_t /* n*/){free(p);//第一級配置器直接使用free釋放}static void* reallocate(void* p,size_t/*old_size*/,size_t new_sz){void* result=realloc(p,new_sz);//當無法滿足要求時,改用oom_reallocif(0==result) result=oom_realloc(p,new_sz);return result;}//此處沒有用new操作符,所以需要模擬出C++new-hander機制static void (* set_malloc_handler(void(*f)()))(){void(*old)()=__malloc_alloc_oom_handler;__malloc_alloc_oom_handler=f;return old;}};//初值為0,由使用者指定,設計“記憶體不足處理常式”是用戶端的責任template<int inst>void(*__malloc_alloc_template<inst>::__malloc_alloc_oom_handler)()=0;//調用使用者指定的錯誤處理程式,如果使用者沒有指定,則直接拋出異常template<int inst>void* __malloc_alloc_template<inst>::oom_malloc(size_t n){void (*my_malloc_handler)();void* result;for(; ;){//不斷嘗試釋放,配置,再釋放,再配置my_malloc_handler=__malloc_alloc_oom_handler;if(0==my_malloc_handler){__THROW_BAD_ALLOC;}(*my_malloc_handler)();//調用處理常式,企圖釋放記憶體result=malloc(n);if(result) return result;}}template<int inst>void* __malloc_alloc_template<inst>::oom_realloc(void* p,size_t n){void(* my_malloc_handler)();void* result;for(;;){my_malloc_handler=__malloc_alloc_oom_handler;if(0==my_malloc_handler){__THROW_BAD_ALLOC;}(*my_malloc_handler)();//調用處理常式,企圖釋放記憶體result=realloc(p,n);//嘗試再次配置記憶體if(result) return result;}}//使用時,直接將inst指定為0typedef __malloc_alloc_template<0> malloc_alloc;//第二級配置器/*解析:第二級配置器,解決兩個問題:一是記憶體片段問題,二是配置時的額外負擔,因為,每次要求配 *置記憶體,都要有一個cookie,記錄記憶體大小,所以要求的記憶體越小,cookie占的比重越大,額外負擔 *越大; *第二級配置器,是預設配置器,當要求的記憶體超過128byte時,移交到第一級配置器 *當小於128byte時,以記憶體池管理:每次配置一大塊記憶體,並維護對應得自由鏈表,下次若再有相同 *大小的記憶體需求,就直接從free-list中撥出,如果客端釋還小額區塊,就由配置器回收到free-list中 *其中這些小額區塊的記憶體需求量會被上調至8的倍數 *第二級配置器共有16個free-list,管理大小為8,16,24,32,40,48,56,64,72,80,88,96,104 *112,120,128byte的區塊 *//*記憶體分三級結構 free-list<--------記憶體池<----------堆空間heap *首先從heap中申請一大塊空間,放在記憶體池中,記憶體池由始末兩個指標劃定; *使用者申請的記憶體區塊是從free-list中申請的,當free-list空間不足時,記憶體池會分配空間給 *free-list,當記憶體池中空間不足時,從heap分配空間給free-list *-——————--——————————————————————————————- *|#0| #1| #2|#3|#4|#5|#6|#7|#8|#9|#10|#11|#12|#13|#14|#15|#16| *-————————————————————————————————————--- *上為freelist結構,每一個位置都儲存一個鏈表,每條鏈表中元素都是不同大小的區塊 *///此處,不知道為什麼使用enum。。。。enum{__ALIGN=8};//小型區塊的上調邊界enum{__MAX_BYTES=128};//小型區塊的上限enum{__NFREELISTS=__MAX_BYTES/__ALIGN};//free-lists個數//忽略多線程template<int inst>class __default_alloc_template{private://ROUND_UP()將bytes上調至8的倍數static size_t ROUND_UP(size_t bytes){return (((bytes)+__ALIGN-1)&~(__ALIGN-1));}//與~(__ALIGN-1)相與導致最小值必然是8//8的倍數private://free-list的節點結構union obj{union obj* free_list_link;char client_data[1];};private://16個free-list,我對volatile不是很熟悉static obj* volatile free_list[__NFREELISTS];//根據byte大小,決定使用第幾號free-liststatic size_t FREELIST_INDEX(size_t bytes){ return (((bytes)+__ALIGN-1)/__ALIGN-1);}//當發現free-list沒有可用性區域塊時,重新填充記憶體static void* refill(size_t n);//配置一大塊空間,可容納nobjs個大小為size的區塊static char* chunk_alloc(size_t size,int& nobjs);//是int&,因為nobjs可能被修改static char* start_free;//記憶體池的開始位置,只在chunk_alloc中變化static char* end_free;//記憶體池的結束位置,只在chunk_alloc中變化static size_t heap_size;public://跟第一級配置器一樣的介面static void* allocate(size_t n);static void deallocate(void* p,size_t n);static void* reallocate(void* p,size_t old_sz,size_t new_sz){ };};template<int inst>char* __default_alloc_template<inst>::start_free=0;template<int inst>char* __default_alloc_template<inst>::end_free=0;template<int inst>size_t __default_alloc_template<inst>::heap_size=0;template<int inst>typename __default_alloc_template<inst>::obj* volatile __default_alloc_template<inst>::free_list[__NFREELISTS]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//free_list[i]為一條鏈表template<int inst>void* __default_alloc_template<inst>::allocate(size_t n){obj* volatile * my_free_list;//my_free_list指向鏈表,鏈表中存放的是obj*obj* result;//大於128 就調用第一級配置器if(n>(size_t)__MAX_BYTES){return(malloc_alloc::allocate(n));}//尋找16個鏈表中適當的一個my_free_list=free_list+FREELIST_INDEX(n);result=*my_free_list;if(result==0){//沒有找到可用的free-list,重新填充free-listvoid* r=refill(ROUND_UP(n));return r;}*my_free_list=result->free_list_link;return (result);};template<int inst>void __default_alloc_template<inst>::deallocate(void* p,size_t n){obj* q=(obj*)p;obj* volatile * my_free_list;if(n>(size_t)__MAX_BYTES){malloc_alloc::deallocate(p,n); return;}my_free_list=free_list+FREELIST_INDEX(n);//調整free-list,回收區塊q->free_list_link=*my_free_list;*my_free_list=q;}//返回一個大小為n的對象,並且有時候為適當的free-1ist增加節點//假設n已經上調至8的倍數template<int inst>void* __default_alloc_template<inst>::refill(size_t n){int nobjs=20;//調用chunk_alloc,嘗試取得nobjs個區塊作為fre-list的新節點char* chunk=chunk_alloc(n,nobjs);obj* volatile * my_free_list;obj* result;obj* current_obj;obj* next_obj;//如果只獲得一個區塊,這個區塊就分配給調用者,free-list無新節點if(1==nobjs)return(chunk);//否則準備調整free-list,接納新節點my_free_list=free_list+FREELIST_INDEX(n);result=(obj*)chunk;*my_free_list=next_obj=(obj*)(chunk+n);//將free-list的各節點穿起來for(int i=1;;i++){//從1開始,因為第0個返回給用戶端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;}//從記憶體池中取空間給free-list//假設size已上調至8的倍數,nobjs按引用傳遞template<int inst>char* __default_alloc_template<inst>::chunk_alloc(size_t size,int& nobjs){char* result;size_t total_byte=size*nobjs;size_t byte_left=end_free-start_free;//記憶體池的剩餘空間if(byte_left>total_byte){result=start_free;start_free+=total_byte;return result;}else if(byte_left>=size){//此時記憶體池中剩餘空間量不能完全滿足需求,但能夠供應一個以上區塊nobjs=byte_left/size;total_byte=size*nobjs;result=start_free;start_free+=total_byte;return result;}else{//記憶體池中連一個區塊都無法提供size_t byte_to_get=2*total_byte+ROUND_UP(heap_size>>4);////以下操作試著讓記憶體池中的殘餘零頭還有利用價值if(byte_left>0){//分配給適當的free-listobj* volatile* my_free_list=free_list+FREELIST_INDEX(byte_left);//調整free-list,容納新節點((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){//malloc失敗obj* volatile* my_free_list;obj* p;//試著檢查我們手上的東西,搜尋適當的區塊,即:尚未使用,且區塊足夠大的free-listfor(int i=size;i<=__MAX_BYTES;i+=__ALIGN){my_free_list=free_list+FREELIST_INDEX(i);p=*my_free_list;if(0!=p){//調整free_list以釋放未用區塊,將這一區塊還給記憶體池*my_free_list=p->free_list_link;start_free=(char*)p;end_free=start_free+i;//現在記憶體池中有足夠一個size的空間了return (chunk_alloc(size,nobjs));}}end_free=0;//這時候確實沒有記憶體可用了//調用第一級配置器的out_of_memory機制start_free=(char*)malloc_alloc::allocate(byte_to_get);//會導致拋出異常或者獲得記憶體}heap_size+=byte_to_get;end_free=start_free+byte_to_get;//現在記憶體池中有足夠的空間了return (chunk_alloc(size,nobjs));}}/*第二級配置器必須遵循的契約就是以8的倍數單位的記憶體配置,例如分配時,100byte,會返回128 *byte,然而回收時,對於100byte,會回收128byte *///第一二級記憶體配置器的代碼已具備,怎麼用呢?//使用時,另封裝有一個介面,使得配置單位由byte轉化為個別元素的大小template<typename T,typename Alloc>class simple_alloc{public:static T* allocate(size_t n)//配置n個T對象{return 0==n ? 0:(T*)Alloc::allocate(n*sizeof(T));}static T* allocate(void)//配置一個T對像{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));}};//使用範例#ifdef __USE_MALLOCtypedef malloc_alloc alloc;//令第一級配置器為預設配置器#else typedef __default_alloc_template<0> alloc;//令第二級為預設配置器#endif
使用範例:
#ifdef __USE_MALLOCtypedef malloc_alloc alloc;//令第一級配置器為預設配置器#else typedef __default_alloc_template<0> alloc;//令第二級為預設配置器#endiftemplate<class T,class Alloc=alloc>class vector{public:typedef T value_type;protected:typedef simple_alloc<value_type,Alloc> data_allocator;};typedef simple_alloc<int,alloc> int_new;int main(){int* s=int_new::allocate(5); s[0]=0;s[1]=1;s[2]=2;s[3]=3;s[4]=4; for(int i=0;i<5;++i){ std::cout<<s[i]; }int_new::deallocate(s,5);char c=getchar();}