众所周知,STL中的空间配置器分为一级空间配置器和二级配置器。第一级配置器以malloc( ),free(),realloc()等C函数执行实际的内存配置、释放、重配置操作,并实际出类似C++ new-handler的机制。
C++ new handler机制是,你可以要求系统在内存配置需求无法被满足时,调用一个你所指定的函数。换句话说,一旦::operator new 无法完成任务,在丢出std::bad_alloc异常状态之前,会先调用由客户指定的处理例程。该处理例程通常即被称为new-handler。
SGI第一级配置器的allocate()和 realloc()都是在调用malloc()和realloc()不成功后,改调用oom_malloc()和oom_realloc()。后两者都有内循环,不断调用“内存不足处理机制”,期望在某次调用之后,获得足够的内存而圆满完成任务。但如果“内存不足处理例程”并未被客户设定,oom_malloc()和oom_realloc()便毫不客气地调用_THROW_BAD_ALLOC,丢出bad_alloc异常信息,或利用exit(1)硬生生中止程序。
下面给出具体的代码:
#ifndef _STL_ALLOC_H_ #define _STL_ALLOC_H_ #if 1 #include<iostream> #include<malloc.h> #include<exception> #include<stdlib.h> #include<string.h> using namespace std; #define __THROW_BAD_ALLOC cerr<<"Out Of Memery."<<endl;exit(1); #elif !defined(__THROW_BAD_ALLOC) #include<iostream.h> #define __THROW_BAD_ALLOC cerr<<"out of memory" << endl;exit(1); #endif //一级空间配置器 template<int inst> class __malloc_alloc_template { private: static void* oom_malloc(size_t); static void* oom_realloc(void*,size_t); //函数指针 static void (*__malloc_alloc_oom_handler)(); public: //空间申请函数 static void* allocate(size_t n) { void *result = malloc(n); if(result == 0) { //第一次申请不成功再次申请 result = oom_malloc(n); } return result; } //空间回收函数 static void deallocate(void* p,size_t n) { free(p); } static void* reallocate(void*p,size_t,size_t new_sz) { void *result = realloc(p,new_sz); if(result == 0){ result = oom_realloc(p,new_sz); } return result; } //set_malloc_handler为一个函数,参数为一个函数指针 static void (*set_malloc_handler(void (*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; //如果my_malloc_handler为0,说明用户没有设置内存不足时的处理函数 //抛出异常 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; } } }
SGI第二级配置器的做法是,如果区域够大,超过128bytes时,就要移交给第一级配置器处理。当区块小于128bytes时,则以内存池管理,此法又称为次层配置:每次配置一大块内存,并维护对应之自由链表(free-list)。下次若再有相同大小的内存需求,就直接从free-list中拨出,如果客端释还小额区块,就由配置器回收到free-list中。为了方便管理,SGI第二级配置器主动将任何小额区块的内存需求量上调8的倍数,并维护16个free-list。
typedef __malloc_alloc_template<0> malloc_alloc; /////////////////////////////////////////////////////////////////////////////// //二级空间配置器 enum{__ALIGN = 8}; //小型区块的上调边界 enum{__MAX_BYTES = 128}; //小型区块的上界 enum{__NFREELISTS = __MAX_BYTES / __ALIGN}; //free-list个数 template<bool threads,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)); } private: //free-list的节点构造 union obj { union obj * free_list_link; char client_data[1]; }; private: //16个free-list static obj * volatile free_list[__NFREELISTS]; //以下的函数根据区块的大小,决定使用第n号的free-list,n从0开始算 static size_t FREELIST_INDEX(size_t bytes) { return ((bytes + __ALIGN-1) / __ALIGN-1); } //内存池的起始位置 static char* start_free; //内存池的结束位置 static char* end_free; static size_t heap_size; private: static void* refill(size_t n); static char* chunk_alloc(size_t size,int &nobjs); public: //空间配置函数 static void* allocate(size_t n) { obj * volatile *my_free_list; obj * result; //当n > 128 时,转到一级空间配置器 if(n > __MAX_BYTES) { return malloc_alloc::allocate(n); } //根据n的值,索引适合的小标 my_free_list = free_list + FREELIST_INDEX(n); result = *my_free_list; //当free-list为空时,准备重新填充free-list if(result == 0){ void *r = refill(ROUND_UP(n)); return r; } *my_free_list = result->free_list_link; return result; } static void deallocate(void *p,size_t n) { obj *q = (obj*)p; obj * volatile * my_free_list; if(n > __MAX_BYTES){ malloc_alloc::deallocate(p,n); return ; } my_free_list = free_list + FREELIST_INDEX(n); q->free_list_link = *my_free_list; *my_free_list = q; } static void* reallocate(void *p,size_t old_sz,size_t new_sz); }; 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> typename __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}; template<bool threads,int inst> void* __default_alloc_template<threads,inst>::refill(size_t n) { int nobjs = 20; char *chunk = chunk_alloc(n,nobjs); 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); for(i = 1;;++i){ 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; } //内存池 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 { //内存池连一个区块的大小都无法提供 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; } //通过malloc申请比较大的空间 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; 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); } } template<bool threads,int inst> void* __default_alloc_template<threads,inst>::reallocate(void *p,size_t old_sz,size_t new_sz) { void * result; size_t copy_sz; if(old_sz > (size_t) __MAX_BYTES && new_sz > (size_t)__MAX_BYTES){ return (realloc(p,new_sz)); } if(ROUND_UP(old_sz) == ROUND_UP(new_sz)) return(p); result = allocate(new_sz); copy_sz = new_sz > old_sz ? old_sz : new_sz; memcpy(result,p,copy_sz); deallocate(p,old_sz); return (result); } ///////////////////////////////////////////////////////////////////////////////////// // #ifdef _USE_MALLOC typedef __malloc_alloc_template<0>malloc_alloc; typedef malloc_alloc alloc; #else typedef __default_alloc_template<0,0>alloc; #endif //////////////////////////////////////////////////////////////////////////////////// // //二次封装 template<class T,class Alloc> class simple_alloc { public: static T * allocate(size_t n) { return 0 == n ? 0 : (T*)alloc::allocate(n * sizeof(T)); } static T *allocate(void) { 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)); } }; #endif