【STL】实现简单的空间配置器
2017-08-08 21:11
597 查看
1、模拟实现STL源码中实现的空间配置器
实现方式:STL源码中的空间配置器的实现分为一级与二级空间配置器;
1.1 一级空间配置器
所谓的一级空间配置器:(实现特点)【1】直接使用malloc函数申请内存
【2】自定义清除函数;(当malloc失败之后,调用清除函数来释放一部分的内存空间,以便malloc的成功)
【3】假设调用清除函数之后,还是malloc失败的话,就需要抛出异常 (bad_alloc)
代码实现:
//实现一级空间配置器 template<int inst> class __Malloc_Alloc_Template { public: static void* allocate(size_t n) { void * result = malloc(n); //要是malloc失败 if(result == NULL) { //调用omm_malloc函数 result = omm_malloc(n); } return result; } //空间释放 static void deallocate(void * del) { //直接调用free free(del); } //设置自定义的清理函数 函数返回的是原来的清理函数 //函数原型的意思就是 //定义一个函数 返回值是 静态的函数指针参数为空 //这个函数的参数为 函数指针参数 为 空 func static void (*set_malloc_handler(void (* func)()))() { void (*old) = ___malloc_alloc_oom_handler; ___malloc_alloc_oom_handler = func; return old; } private: static void (*___malloc_alloc_oom_handler)(); //表示当前malloc失败内存不足,调用自定义的清理函数 static void * omm_malloc(size_t n) { void (*my_malloc_handler)(); void *result =NULL; while(1) { my_malloc_handler = ___malloc_alloc_oom_handler; //表示的当前的清理函数为空的话 if(my_malloc_handler == NULL) { //直接抛出异常 throw bad_alloc(); } //调用清理函数 my_malloc_handler(); //调用完之后重新malloc来申请空间 result = malloc(n); if(result) { return result; } } } }; template<int inst> void (*__Malloc_Alloc_Template<inst>::___malloc_alloc_oom_handler)()=NULL;
1.2 二级空间配置器
实现方式:在这里引入内存池与自由链表来实现二级空间配置
二级空间配置器的优缺点:
【内存池】
我们都知道,编译器在用户态与内核态之间的转换是十分的耗时的。所以,我们可以事先申请一大片的内存空间;将它保存起来,等到需要动态开辟空间的时候,直接到这块空间上切一块,
直到用完之后,重新申请;
【自由链表】
何为自由链表?就是使用链表将所需要大小的内存块链到一块。
SGI二級空间配置器保存的自由链表的空间块大小都是8的倍数;并且会將任何小区域的内存块的大小調至8 的倍數(
各自管理大小分別為 8, 16, 24, 32, 40, 48, 56, 64, 72,80, 88, 96, 104, 112, 120, 128 bytes,
当需要的空间大小超过128byte时,直接调用一级空间配置器。
实现代码:
//实现二级空间配置器 template<bool threads,int inst> class __Default_Alloc_Template { public: static void * allocate(size_t n) { //要是当前要开辟的空间大于128的则直接使用 一级空间配置器来malloc开辟 if(n >(size_t) __MAX_BYTES) { return __Malloc_Alloc_Template<0>::allocate(n); } //小于128的直接到自由链表中获取 //index:得到大小为n的空间要在自由链表的下标为index处获得 size_t index = FREELIST_INDEX(n); //result表示的就是自由链表的第一结点; obj* result = free_list[index]; if(result== NULL) { //要是当前result 为NULL 表示的当前的自由链表中没有当前大小的内存块 //需要从内存池中进行切分 return refill(ROUND_UP(n)); } //表示的当前的自由链表中有此大小的内存块;直接拿到; //更新当前的自由链表 free_list[index] = result->free_list_link; return result; } //refill函数表示的是要是当前的自由链表中没有此大小的内存块 //需要来从内存池中来切分 static void * refill(size_t n) { //表示现在需要到内存池中切分20个这样大小的内存块 size_t nobjs= 20; //chunk_alloc为切分函数 char * chunk = chunk_alloc(n,nobjs); //nobjs表示输出函数 返回的是实际切分到的内存块个数 if(nobjs == 1) { //要是只切分到一个内存块的话,直接返回就好 return (void *) chunk; } //其他就表示的是得到的内存块的个数大于1 //需要将剩余的内存块挂接到的自由链表中 size_t index = FREELIST_INDEX(n); //cur表示的就去除第一个要返回的内存块,的第二个内存块 obj * cur = (obj*)chunk + n; free_list[index] = cur; for(size_t i = 2;i < nobjs;++i) { obj * next = (obj*)(chunk + i*n); cur->free_list_link = next; cur= next; } cur->free_list_link = NULL; return (void *)chunk; } //chunk_alloc函数来从内存池中切分大小为size的内存块 需要的个数为nobjs //nobjs传引用 ,返回的是实际切分的内存块个数 static char * chunk_alloc(size_t size,size_t & nobjs) { char * ret = NULL; size_t total_bytes = size * nobjs;//表示总共需要切分的内存大小 size_t bytes_left = end_free - start_free;//表示的是当前的内存池的大小 if(bytes_left > total_bytes) { //表示的是当前的内存池足够切分当前需要的内存块 ret = start_free; start_free += total_bytes;//更新内存池的大小 return ret; } else if(bytes_left >=size) { //表示的是当前的内存池内不足以开辟当前需要的内存块 //但足够开辟至少一个内存块 nobjs = bytes_left /size;//表示的当前切分到的内存块个数 ret =start_free ; start_free += (nobjs*size); return ret; } else { //表示的是当前的内存池一个内存块都不够切分 //则需要重新malloc来开辟空间 if(bytes_left != 0)//当前的内存池大小不为0 ,需要处理剩余的内存 { //将当前内存池中的空间合理的分配得到自由链表中 int index = FREELIST_INDEX(bytes_left); //头插到到自由链表中 ((obj*)start_free)->free_list_link = free_list[index]; free_list[index] = (obj*)start_free; } //重新开辟新空间 size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size>>4);//bytes_to_get表示新开辟空间的大小 start_free = (char*)malloc(bytes_to_get); if(start_free == NULL) { //表示的是malloc失败,系统已经开不出空间 //现在只能从自由链表中来看看 for(size_t i = FREELIST_INDEX(size);i < (size_t)__NFREELISTS;++i ) { if(free_list[i] != NULL) { start_free = (char*)free_list[i]; end_free = start_free + (i+1)*__ALIGN; return chunk_alloc(size,nobjs); } } end_free = 0 ; start_free = (char*)__Malloc_Alloc_Template<0>::allocate(bytes_to_get); } heap_size = heap_size+ bytes_to_get; end_free = start_free + bytes_to_get; return chunk_alloc(size,nobjs); } } static void deallocate(void * p,size_t n) { obj * del =(obj*) p; //要是当前释放的空间大小大于128直接使用一级空间配置器释放 if( n >(size_t)__MAX_BYTES ) { __Malloc_Alloc_Template<0>::deallocate(p); return ; } //将释放的空间连到自由链表上 size_t index = FREELIST_INDEX(n); del->free_list_link = free_list[index]; free_list[index] = del; } private: //表示的小型的区域的上调边界 enum {__ALIGN = 8}; //自由链表的链接的内存的最大值 enum {__MAX_BYTES =128}; //表示的是自由链表的个数 enum {__NFREELISTS = __MAX_BYTES / __ALIGN}; //ROUND_UP函数将bytes上调到 8 的倍数 static size_t ROUND_UP(size_t bytes) { //任何数+ 7 将这个数 与上 FFF0 return (bytes +__ALIGN-1)& ~(__ALIGN-1); } //根据要开辟的空间大小来得到 该大小在自由链表中的下标 static size_t FREELIST_INDEX(size_t bytes) { return (bytes + __ALIGN-1)/(__ALIGN-1); } private: //链表的结点 union obj { union obj * free_list_link ;//自由链表 char client_data[1]; }; //自由链表数组 ,内部保存一个个的内存块链表 static obj * free_list[__NFREELISTS]; static char* start_free;//内存池的起点 static char* end_free;//内存池的结尾 static size_t heap_size ;//内存池的大小; }; 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> typename __Default_Alloc_Template<threads,inst>::obj* __Default_Alloc_Template<threads,inst>::free_list[__Default_Alloc_Template<threads,inst>::__NFREELISTS] = {0}; template<bool threads,int inst> size_t __Default_Alloc_Template<threads,inst>::heap_size = 0;
2、空间配置器的包装与运用方式
实现:
#ifdef __USE_MALLOC typedef __Malloc_Alloc_Template<0> alloc; #else typedef __Default_Alloc_Template<false,0> alloc; #endif //__USE_MALLOC template<class T ,class Alloc =alloc> class simple_alloc { public: //n表示的是申请T类型的空间的个数 static T * allocate(size_t n) { return 0==n ? NULL : (T *)Alloc::allocate(n*sizeof(T)); } //表示申请一个T类型的空间 static T * allocate(void) { return (T*)Alloc::allocate(sizeof(T)); } static void deallocate(T * del) { Alloc::deallocate(del,sizeof(T)); } static void deallocate(T * del,size_t n) { if( n!=0) Alloc::deallocate(del,sizeof(T)*n); } }; #include<vector> void Test() { int * array = simple_alloc<int,alloc>::allocate(2); array[0]=0; array[1]=1; cout<<array[0]<<endl; cout<<array[1]<<endl; }
相关文章推荐
- 1.简单的空间配置器实现
- 【STL源码剖析读书笔记】自己实现简单的空间配置器MyAllocator
- 【STL源码剖析读书笔记】自己实现简单的空间配置器MyAllocator
- STL中空间配置器(allocator)的简单实现
- STL中的空间配置器allocator的实现原理及源码剖析
- STL之简单空间适配器实现
- 【C++/STL】list的实现(采用空间配置器和迭代器)
- STL源码分析--空间配置器的底层实现 (二)
- c++实现一个简单的空间配置器allocator
- javascript实现简单的命名空间
- STL 简单红黑树的实现
- 【STL】空间配置器
- STL源码剖析学习二:空间配置器(allocator)
- STL源码:空间配置器(一)SGI的空间配置器
- STL中vector的内存管理与简单代码实现
- STL之空间配置器
- STL(五):连续空间的二维数组实现
- 一步一步写STL:空间配置器(1)
- C++STL学习(13)STL深入(2) SGI STL空间配置器
- stl--分析空间配置器及源码实现