stl 空间配置器理解
理解了一下stl的空间配置器,发现一个比较好的学习方法,跟着代码自己也跟着写一遍,顺便加些注释,可以更加帮助自己理解。
如new,delete一般,分为两个步骤,1,配置空间,2,构造对象(1,析构对象,2,释放空间)
一。构造和析构的基本工具(construct,destroy)
1,construct(构造)
template<class T1,class T2> inline void construct(T1 * p,const T2 & value){ new (p) T1(value); //在T1类型的p处用T2类型的value进行初始化 }
2,destroy(析构)
析构有两个版本
1,第一个版本,接收一个对象指针,调用其析构函数
//1
template<class T> inline void destroy(T * pointer){ pointer->~T(); }
2,第二个版本,接受一个迭代器区间
//2
template<class ForwardIterator> inline void destroy(ForwardIterator first,ForwardIterator last){ _destroy(first,last,value_type(first)); } template<class ForwardIterator,class T> inline void _destroy(ForwardIterator first,ForwardIterator last,T *){ typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor; _destroy_aux(first,last,trivial_destructor()); } //2.1 template<class ForwardIterator> inline void _destroy_aux(ForwardIterator first,ForwardIterator last,__false_type){ for(;first < last ;first++) destroy(&*first); } //2.2 template<class ForwardIterator> inline void _destroy_aux(ForwardIterator first,ForwardIterator last,__true_type){ // trival type : do nothing }
判断元素的数值型别是否是trivial destructor,不重要的类型,如果依次对每个做析构,很浪费效率,所以对于其不重要的类型,就用不着做什么了,do nothing
为了针对原生指针(char * ,wchar_t*两种)还有其特化版本,同样此也是不重要(trivial)的类型,不用做什么了
//3 特化 inline void destroy(char * ,char * ){} //4 特化 inline void destroy(wchar_t * ,wchar_t *){}
二。空间的配置和释放
空间的配置和释放分为两级,第一级配置器仅是对于malloc和free的封装,并没有对效率的强化。因此有了第二级配置器,用了内存池的思想,提高了效率
1,第一级配置器
#if 0 #include <new> #define __THROW_BAD_ALLOC throw bad_alloc #elif !define(__THROW_BAD_ALLOC) #include <iostream.h> #define __THROW_BAD_ALLOC cerr<<"out of memory"<<endl;exit(1); #endif template<class inst> class __malloc_alloc_template{ private: //oom : out of memory ,these funcs to handle oom static void * oom_malloc(size_t); static void * oom_realloc(void * ,size_t); static void (* __malloc_alloc_oom_handler)(); //在oom_xxx中调用处理malloc失败的情况 public: static void * allocate(size_t n){ void *result = malloc(n); if( 0 == result) result = oom_malloc(n); return result; } static void deallocate(void * p,size_t){ free(p); } static void * reallocate(void * p,size_t ,size_t new_sz){ void * result = realloc(p,new_sz); if( 0 == result) result = oom_realloc(p,new_sz); return result; } static void (* set_malloc_handler(void (*f)()))(){ //配置内存失败时会调用oom_XXX,其中调用f处理例程,此处可自己设置处理例程 void (*old)() = __malloc_alloc_oom_handler; __malloc_alloc_oom_handler = f; return old; } }; 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_realloc_handle)(); void * result; for(;;){ __my_realloc_handle = __malloc_alloc_oom_handler; if(0 == __my_realloc_handle){__THROW_BAD_ALLOC} (*__my_realloc_handle)(); result = realloc(p,n); if(result) return result; } } typedef __malloc_alloc_template<0> malloc_allloc;
2,第二级配置器
主要过程:如果分配的区块够大,大于128bytes,就移交给第一级配置器处理,当区块小于128bytes时,以内存池管理,每次配置一大块内存,并维护对应之自由链表。下次再有相同大小的内存需求,直接从内存池中在取出即可。
二级配置器主动将所需内存调整至8的倍数。二级配置器内维护16个链表,各自管理8,16,24,。。。128大小的内存块。
1.对应的free list有可用的区块,直接拿过来用。
2.如果没有可用的区块,调用函数refill()为free list重新填充空间。
refill的工作:从内存池中取得内存,并依次分割成相应大小的块交给相应的链表维护,默认再取得20个区块,但实际情况还要在chunk_alloc中判断,可能不能完全取到20个区块。
在refill中调用chunk_alloc取得空间;
chunk_alloc中分为三种情况,
1.内存池中的内存容量完全够需求
2.内存池剩余空间不能完全满足需求,但足够供应一个以上的区块
3.连一个区块都不能获得
3.1.先将剩余的一些小空间交于其他的链表管理,省的浪费
3.2.从heap中分配空间补充内存量,补充内存池。
3.3.补充内存池之后,调整start_free,end_free,递归调用chunk_alloc来继续分配空间。
enum {__ALIGN = 8}; enum {__MAX_BYTES = 128}; enum {__NFREELISTS = __MAX_BYTES/__ALIGN}; template<bool threads,int inst> class __default_alloc_template{ private: static size_t ROUND_UP(size_t bytes){ //调整至8的倍数 return ((bytes + __ALIGN -1) & ~(__ALIGN-1)); } private: union obj{ union obj * free_list_link; char client_data[1]; }; private: static obj * volatile free_list[__NFREELISTS]; static size_t FREELIST_INDEX(size_t bytes){ //决定使用第几号free_list return (bytes + __ALIGN -1)/__ALIGN - 1; } //返回一个大小为n的对象,并可能将此加入到free_list中 static void * refill(size_t n); //配置空间,内存池 static char * chunk_alloc(size_t size,int & nobjs); static char * start_free; //内存池起始位置 static char * end_free; //内存池结束位置 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 oldsz,size_t newsz); }; 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; template<bool threads,int inst> __default_alloc_template<threads,inst>::obj * volatile __default_alloc_template<threads,inst>::free_list[__NFREELISTS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //1 首先判断区块大小,若大于128调用第一级配置器 //2 小于128,从free_list中取(取之前是对调整后的区块进行取),有则取,没有则将区块调整至8的倍数,调用refill填充空间 template<bool threads,int inst> void * __default_alloc_template<threads,inst>::allocate(size_t n){ obj * volatile * my_free_list; obj * result; if( n > (size_t)__MAX_BYTES) return (malloc_allloc::allocate(n)); my_free_list = free_list+FREELIST_INDEX(n); result = * my_free_list; if(result == 0){ //例:当内存池中96bytes的区块取光后,就refill重新建立 void * r = refill(ROUND_UP(n); return r; } *my_free_list = result->free_list_link; return result; } template<bool threads,int inst> void __default_alloc_template<threads,inst>::deallocate(void * p,size_t n){ obj * q = (obj *) p; obj * volatile * my_free_list; if(n > __MAX_BYTES){ malloc_allloc::deallocate(p,n); return; } my_free_list = free_list+FREELIST_INDEX(n); q->free_list_link = *my_free_list; *my_free_list = q; } template<bool threads,int inst> void * __default_alloc_template<threads,inst>::refill(size_t n){ int nobjs = 20; char * chunk = chunk_alloc(n,nobjs); //默认申请20个块 obj * volatile * my_free_list; obj * result; obj * current_obj,*nextobj; int i; if(1 == nobjs) return chunk; my_free_list = free_list + FREELIST_INDEX(n); result = (obj * )chunk; //等待返回给需求者 * my_free_list = nextobj = (obj * )(chunk + n); for(i = 1;;i++){ current_obj = nextobj; nextobj = (obj * )((char *)nextobj+n); //不断进行分割 if(nobjs - 1 == i){ //分割结束 current_obj->free_link_list = 0; break; } else current_obj->free_link_list = nextobj; } return result; } template<bool threads,int inst> char * __default_alloc_template::chunk_alloc(size_t size,int & nobjs){ char * result; size_t total_bytes = size * nobjs; size_t bytesleft = end_free - start_free; //1内存池剩余空间完全满足需求量 if(bytesleft >= total_bytes){ result = start_free; start_free+=total_bytes; //提供区块后,调整start_free return result; } //2 不能完全满足需求量,但能供应一个及以上 else if(bytesleft >= size){ nobjs = bytesleft/size; total_bytes = size*nobjs; result = start_free; start_free+=total_bytes; return result; } //3 连一个也不能提供 else{ size_t bytes_to_get = 2*total_bytes + ROUND_UP(heap_size>>4); if(bytesleft > 0){ //将剩余的一小点空间配给别的list obj * volatile * my_free_list = free_list+FREELIST_INDEX(bytesleft); ((obj*)start_free)->free_list_link = *my_free_list; *my_free_list = (obj *)start_free; //如果不是跟 } start_free = (char *)malloc_allloc(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; //递归调用自己,为了修正nobjs return chunk_alloc(size,nobjs); //调整了start_free和end_free了,就再次进行分配,至少能分到一个区块了 } } end_free = 0; start_free = (char * )malloc_allloc::allocate(bytes_to_get); } heap_size += bytes_to_get; end_free = start_free+bytes_to_get; return chunk_alloc(size,nobjs); //调整了start_free和end_free了,就再次进行分配,至少能分到一个区块了 } }