剖析——SGI版本下的空间配置器
来源:互联网 发布:g92内螺纹编程实例 编辑:程序博客网 时间:2024/05/17 04:14
1.为什么STL中会把空间配置器单独给出来呢?
2.空间配置器使用接口
3.一二级空间配置器的实现
3.1一级空间配置器
简介:简单封装了malloc /free进行实现,并给出了set_new_handler 函数,在 malloc 失败时,调用句柄函数或选择抛出异常
#include<new>typedef void(*NewHandlerFunPtr)();template<int inst>class mallocAllocTemplate//一级空间配置器{private: //以下函数用来处理内存不足的情况 static void* oom_malloc(size_t ); static void* oom_realloc(void* , size_t); static NewHandlerFunPtr malloc_alloc_oom_hanlder;public: static void* allocate(size_t n) { void * res = malloc(n); if (res == NULL) { res = oom_malloc(n); } return res; } static void* reallocate(void* p,size_t new_size) { void * res = realloc(p,new_size); if (res == NULL) { res = oom_realloc(p,new_size); } return res; } void deallocate(void* p) { free(p);//第一级空间配置器直接使用free() } //用户设置自己的out_of_memory handler static NewHandlerFunPtr set_malloc_handler(NewHandlerFunPtr p) { NewHandlerFunPtr old = malloc_alloc_oom_hanlder; mallocMllocMemplate = p; return old; }};//初始化处理函数指针template<int inst>NewHandlerFunPtr mallocAllocTemplate<inst>::malloc_alloc_oom_hanlder = 0;template<int inst>void* mallocAllocTemplate<inst>::oom_malloc(size_t n){ NewHandlerFunPtr my_alloc_handler; void* res; for (;;)//这样死循环效率高 { my_alloc_handler = malloc_alloc_oom_handler; if (0 == my_alloc_handler) { throw std::bad_alloc(); } (*my_alloc_handler)(); res = malloc(n); if (res) { return res; } }}template<int inst>void* mallocAllocTemplate<inst>::oom_realloc(void* p,size_t n){ NewHandlerFunPtr my_alloc_handler; void* res; for (;;) { my_alloc_handler = malloc_alloc_oom_handler; if (0 == my_alloc_handler) { throw std::bad_alloc(); } (*my_alloc_handler)(); res = realloc(p,n); if (res) { return res; } }}
3.2二级空间配置器实现原理详解
维护了一个内存池和一张自由链表,哈希链实现快速定址。(头删 头插操作进行分配内存以及释放)
//二级空间配置器的实现//思路:如果申请的空间足够大(>128byte),移交第一级空间配置器//如果较小,使用二级空间配置器//实现以下几个函数enum{_ALIGN=8};enum{_MAXBYTES=128};//自由链表中最大块的大小是128enum{_NFREELISTS=16};//自由链表的长度,等于_MAXBYTES/_ALIGNtemplate<bool threads, int inst>class _DefaultAllocTemplate{ typedef mallocAllocTemplate<inst> malloc_alloc;public: union _Obj //自由链表的结点类型 { _Obj* _freeListLink;//指向自由链表的指针 char _clientData[1];//用户可以使用的地址 };private: static char* _startFree;//内存池的起始地址 static char* _endFree;//内存池的结束地址 static size_t _heapSize;//记录内存池已经向系统申请了多大的内存 static _Obj* volatile _freeList[_NFREELISTS];//自由链表private: static size_t _GetFreeListIndex(size_t bytes)// 获取这个字节在自由链表中的位置 { return (bytes + (size_t)_ALIGN - 1) / (size_t)_ALIGN - 1; } static size_t _GetRoundUp(size_t bytes)// 对这个字节向上取整8的倍数 { return (bytes + (size_t)_ALIGN - 1)&(~(_ALIGN-1)); } //返回大小n的对象,并可能加入大小为n的其他区块到free list static void* refill(size_t n); //配置一大块空间,可容纳nobjs个大小为“size”的区块 //如果配置nobjs个区块有所不便,nobjs可能会降低 static char* chunk_alloc(size_t size, int& nobjs);public: static void* allocate(size_t n); static void dealloccate(void*p, size_t n); static void* reallocate(void*p, size_t old_sz,size_t new_sz);};//static data member 的定义与初值设定template<bool threads, int inst>char* _DefaultAllocTemplate<threads, inst>::_startFree = 0;template<bool threads, int inst>char* _DefaultAllocTemplate<threads, inst>::_endFree = 0;template<bool threads, int inst>size_t _DefaultAllocTemplate<threads, inst>::_heapSize = 0;template<bool threads, int inst>typename _DefaultAllocTemplate<threads, inst>::__Objs*volatile_DefaultAllocTemplate<threads, inst>::_freeList[_NFREELISTS] = { 0 };template<bool threads, int inst>static void* _DefaultAllocTemplate<threads, inst>::allocate(size_t n)//分配空间{ void* ret; //先判断要分配的空间是否大于128字节 if (n > _MAXBYTES) { ret=malloc_alloc::_Allocate(n); } //否则就取自由链表中找 _Obj*& myfreeList = _freeList + _GetFreeListIndex(n); _Obj* result = myfreeList; if (result == 0)//如果这个结点下面没有挂内存,则就要去内存池申请 { ret =refill(_GetRoundUp(n)); //去内存池中申请 } else { //头删 myfreeList = myfreeList->_freeListLink; ret = result; } return ret;}template<bool threads, int inst>static void _DefaultAllocTemplate<threads, inst>::dealloccate(void*p, size_t n)//释放空间{ //先判断这个字节大小 if (n > _MAXBYTES)//如果大于128字节,调用一级空间配置器的释放函数 { malloc_alloc::deallocate(p); } else //将这段内存回收到自由链表中 { //头插 _Obj* q=(_Obj*)p; q=q->_freeListLink; }}template<bool threads, int inst>static void* _DefaultAllocTemplate<threads, inst>::reallocate(void*p, size_t old_sz, size_t new_sz);//重新分配空间template<bool threads, int inst>void* _DefaultAllocTemplate<threads, inst>::refill(size_t n)//重新分配空间{ int nobjs = 20; char* chunk=chunk_alloc(n,nobjs);//向内存池中一次索要20个,返回1-20 //为了效率 if (nobjs == 1) { return chunk; } _Obj*ret = (_Obj*)chunk;//将申请的第一个对象作为返回值,将剩余的nobjs-1个挂接到自由链表中 _Obj* my_free_list = _free_list +_GetFreeListIndex(n); my_free_list = (_Obj*)(chunk + n); _Obj* cur = my_free_list; _Obj* next = cur; cur->_freeListLink = 0; for (int i = 1; i < nobjs; i++) { next = (_Obj*)((char*)next + n); cur->_freeListLink = next; cur = next; } return ret;}//向系统申请内存template<bool threads, int inst>char* _DefaultAllocTemplate <threads, inst> ::chunk_alloc(size_t size, int& nobjs){ char* result = 0; size_t totalBytes = size* nobjs; size_t leftBytes = _endFree - _startFree; if (leftBytes >=totalBytes)//如果剩余内存大于总请求内存 { result = _startFree; _startFree += totalBytes; nobjs = 20; return result; } else if (leftBytes > size)//如果剩余内存足够分=分配一个size { nobjs = (int)(leftBytes / size); result = _startFree; _startFree += (nobjs*size); return result; } else//内存池中一个size也分配不出来 { //内存池开辟新的容量 size_t NewBytes = 2 * totalBytes + _GetRoundUp(_heap.size>>4); if (leftBytes > 0)//剩余的内存挂到自由链表上 { _Obj* myFreeList = _freeList + _GetFreeListIndex(leftBytes); ((_Obj*)_startFree)->_freeListLink = myFreeList; myFreeList = (_Obj*)_startFree; } //开辟新的内存 _startFree = (char*)malloc(NewBytes); if (0 == _startFree)//如果开辟失败 { //如果开辟失败,表明系统已经没有内存了,这时候,就要到自由链表中找一块比n //还大的内存块,如果还没有的话,那就只能调用一级空间配置器 size_t index = _GetFreeListIndex(size); for (; i < (size_t)_NFREELISTS; index++) { _Obj* p = _freeList + index; if (!p)//自由链表中找到一块合适的内存 { //摘下来放到内存池 _startFree = (char*)p; _endFree = _startFree + (index + 1) >> 3; //内存池中已经放入内存,可以进行切割分配了 return chunk_alloc(size,nobjs); } } //要是还找不到内存,只能交给一级空间配置器 _endFree = NULL; _startFree = (char*)malloc_alloc::allocate(NewBytes);; } //开辟成功的,就更新heapSize(记录总共向系统申请了多少内存),更新_endFree _heapSize += NewBytes; _endFree = _startFree + NewBytes; return chunk_alloc(size,nobjs);//现在内存池中可以去分配内存了 }}
空间配置器缺点:
1.有内碎片的问题
2.内存池在结束之前不能释放,被分割为小块的内存一直挂在自由链表下面,会使得内存的占用率过高。(因为这个时候你无法知道释放所需要的头部是哪一块)
解决方案:
①在二级空间配置器的内存池上面设置一个分配内存的上限,也就是当 _heapsize 到达一定的值时,程序就会自动的抛出异常,提醒用户二级空间配置器占用的内存过多了?
②实现一个机制,保存每次 malloc 出来的空间的首地址,并记录开辟的空间在自由链表中(已分为小块)挂的位置。当发现开辟的空间分成小块全都挂在自由链表下时,说明可以释放空间了,现将每个小块从自由链表中删除,然后 free 。
阅读全文
0 0
- 剖析——SGI版本下的空间配置器
- SGI空间配置器
- SGI STL空间配置器(STL源码剖析):
- SGI STL 空间配置器(allocator)源码剖析
- SGI STL V3.2 源码剖析 - 空间配置器
- SGI STL空间配置器(STL源码剖析)
- SGI STL 空间配置器(allocator)源码剖析
- 【STL】SGI版STL空间配置器剖析+简单实现
- STL源码剖析 [简单的SGI STL空间配置器](defalloc.h)
- SGI STL的空间配置器alloc
- SGI版的空间配置器
- STL源码——SGI 空间配置器
- STL——空间配置器(SGI-STL)
- 从轮子造起——SGI-STL空间配置器
- STL一级空间配置器(SGI版本)
- STL源码剖析学习笔记之具备次配置力(sub-allocation)的SGI空间配置器
- SGI-STL空间配置器
- SGI特殊的空间配置器 std::alloc
- 学习python之积累别人的好东西
- Eyepetizer-in-Kotlin:一款简约的小视频app,带你走进kotlin
- 《reinforcement learning:an introduction》第三章《Finite Markov Decision Processes》总结
- 解决Line XX:StartTag:invalid element Name问题
- springMVC固定文件名下载
- 剖析——SGI版本下的空间配置器
- [Leetcode] 289. Game of Life 解题报告
- 套接字网络编程基础(四)
- zTree应用实例详讲(1)
- vuex 小结
- rabbitMQ、activeMQ、zeroMQ、Kafka、Redis 比较
- Spark streaming + kafka 运行时报 Too many open files错误的解决方法
- 从菜鸟到入门(三)- 文档撰写
- NYOJ90 整数划分(经典递归和dp)