SGI STL 的 一,二级配置器浅析
2016-04-25 17:15
288 查看
补充知识:
由于小型区块可能会造成内存破损,所以SGI设计了双层配置器,第一级配置器直接使用malloc()和free(),第二级配置器则根据配置区块的大小采用不同策略(若区块大小大于128bytes就调用一级配置器,若小于128bytes就调用二级配置器(内存池处理))
SGI STL 的第一级配置器 和 第二级配置器的关系
<一> 第一级配置器的工作机制:
allocate()和reallocate()函数在调用malloc()和realloc()失败后,调用oom_malloc()和oom_realloc(),oom_malloc()和oom_realloc()函数内都有内循环,不断的调用“内存不足处理例程”,期望在某次调用后可以获得足够的内存;但若内存不足处理例程客端没有设定,oom_malloc()和oom_realloc()会直接调用__THROW_BAD_ALLOC抛出bad_alloc异常信息,或者利用exit(1)直接终止程序。
第一级配置器的代码如下:
测试代码如下:
<二>第二级配置器的工作机制:若需要的区块的大小超过128bytes,直接转交第一级配置器处理;若区块小于128bytes就调用二级配置器;二级配置器维护着16个free_lists(自由链表),各自管理的大小依次为8的整数倍(即8, 16, 24, 32, 40, 48, 56, 64,72, 80, 88, 96,104, 112, 120, 128bytes的区块)
(1)第一次使用,内存池没有可用的空间
allocate()函数首先判断区块的大小决定用哪一级的配置器,大于128bytes就用一级配置器,否则就用二级配置器并根据区块大小找到相应的free_list,若free_list内有可用的区块,就直接使用,若free_list内没有可用的区块,就将区块大小上调至8的倍数,然后调用refill()函数填充空间,refill()调用内存池函数chunk_alloc()函数来填充空间,第一次调用chunk_alloc()时内存池是没有空间的,因此chunk_alloc()在heap(堆)上申请2倍的需求量(total_bytes)再加上一个附加量(heap_size>>4),之后又回调chunk_alloc()函数修改nobjs;若内存池剩余空间大小大于需求量(total_bytes = nobjs * n),则将内存池的开始位置返回给refill()函数,并将内存池开始位置移动到total_bytes大小后的位置;返回至refill()后将0位置的区块给予客端,并将剩下的区块用free_list_link链接起来放在当前的free_list下;
(2)内存池有可用空间,但是剩余的空间不够20个区块所需的空间
则当refill()函数调用chunk_alloc()函数时,如果剩余内存池空间足够供应一个或一个以上的区块,就返回nobjs块的区块大小,并将一块返回给客端,将剩余的区块链接起来,放在当前的free_list下;若只返回了一个区块的大小,就直接将该区块返回给客端,free_list没有新的节点可以链接;
(3)内存池剩余的空间不足一个区块的大小
先将内存池剩余的空间配置给合适的free_list;
然后重新在heap上申请需求量的2倍加附加量(heap_size>>4)的空间,并将total_bytes大小的空间返回给refill()函数,refill()函数将其中的一块区块给予客端,之后将剩余的区块依次链接起来放在对应的free_list下;
(4)内存池没有剩余空间,heap也没有可以申请的空间
就在free_list中寻找可用的区块
(5)内存池没有剩余空间,heap上也无法申请到空间,并且free_list中也没有可用的合适区块
就调用一级配置器,看看内存处理例程能否得到足够的空间,可以达到就成功,不可以就会抛出bad_alloc异常
第二级配置器的代码:
测试代码如下:
由于小型区块可能会造成内存破损,所以SGI设计了双层配置器,第一级配置器直接使用malloc()和free(),第二级配置器则根据配置区块的大小采用不同策略(若区块大小大于128bytes就调用一级配置器,若小于128bytes就调用二级配置器(内存池处理))
SGI STL 的第一级配置器 和 第二级配置器的关系
<一> 第一级配置器的工作机制:
allocate()和reallocate()函数在调用malloc()和realloc()失败后,调用oom_malloc()和oom_realloc(),oom_malloc()和oom_realloc()函数内都有内循环,不断的调用“内存不足处理例程”,期望在某次调用后可以获得足够的内存;但若内存不足处理例程客端没有设定,oom_malloc()和oom_realloc()会直接调用__THROW_BAD_ALLOC抛出bad_alloc异常信息,或者利用exit(1)直接终止程序。
第一级配置器的代码如下:
#if 1 #include <iostream> #include <malloc.h> #include <stdlib.h> using namespace std; #define __THROW_BAD_ALLOC cerr<<"out of memory."<<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(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)()))() { 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_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; } } }
测试代码如下:
void my_new_handler() { cout<<"OMG, Out Of Memory."<<endl; //exit(1); } int main() { __malloc_alloc_template<0>::set_malloc_handler(my_new_handler); int *p = (int*)__malloc_alloc_template<0>::allocate(sizeof(int)*100000000000000000); *p = 100; cout<<*p<<endl; __malloc_alloc_template<0>::deallocate(p,0); return 0; }
<二>第二级配置器的工作机制:若需要的区块的大小超过128bytes,直接转交第一级配置器处理;若区块小于128bytes就调用二级配置器;二级配置器维护着16个free_lists(自由链表),各自管理的大小依次为8的整数倍(即8, 16, 24, 32, 40, 48, 56, 64,72, 80, 88, 96,104, 112, 120, 128bytes的区块)
(1)第一次使用,内存池没有可用的空间
allocate()函数首先判断区块的大小决定用哪一级的配置器,大于128bytes就用一级配置器,否则就用二级配置器并根据区块大小找到相应的free_list,若free_list内有可用的区块,就直接使用,若free_list内没有可用的区块,就将区块大小上调至8的倍数,然后调用refill()函数填充空间,refill()调用内存池函数chunk_alloc()函数来填充空间,第一次调用chunk_alloc()时内存池是没有空间的,因此chunk_alloc()在heap(堆)上申请2倍的需求量(total_bytes)再加上一个附加量(heap_size>>4),之后又回调chunk_alloc()函数修改nobjs;若内存池剩余空间大小大于需求量(total_bytes = nobjs * n),则将内存池的开始位置返回给refill()函数,并将内存池开始位置移动到total_bytes大小后的位置;返回至refill()后将0位置的区块给予客端,并将剩下的区块用free_list_link链接起来放在当前的free_list下;
(2)内存池有可用空间,但是剩余的空间不够20个区块所需的空间
则当refill()函数调用chunk_alloc()函数时,如果剩余内存池空间足够供应一个或一个以上的区块,就返回nobjs块的区块大小,并将一块返回给客端,将剩余的区块链接起来,放在当前的free_list下;若只返回了一个区块的大小,就直接将该区块返回给客端,free_list没有新的节点可以链接;
(3)内存池剩余的空间不足一个区块的大小
先将内存池剩余的空间配置给合适的free_list;
然后重新在heap上申请需求量的2倍加附加量(heap_size>>4)的空间,并将total_bytes大小的空间返回给refill()函数,refill()函数将其中的一块区块给予客端,之后将剩余的区块依次链接起来放在对应的free_list下;
(4)内存池没有剩余空间,heap也没有可以申请的空间
就在free_list中寻找可用的区块
(5)内存池没有剩余空间,heap上也无法申请到空间,并且free_list中也没有可用的合适区块
就调用一级配置器,看看内存处理例程能否得到足够的空间,可以达到就成功,不可以就会抛出bad_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) { return ((bytes + __ALIGN - 1) & ~(__ALIGN - 1)); //将byte提升为8的倍数 } private: union obj { union obj * free_list_link; char client_data[1]; }; private: static obj * volatile free_list[__NFREELISTS]; //free_list是一个可变的二级指针, 16个自由链表 static size_t FREELIST_INDEX(size_t bytes) //根据区块大小,决定使用第几个free_list { 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; if(n > __MAX_BYTES){ return malloc_alloc::allocate(n); } my_free_list = free_list + FREELIST_INDEX(n); //在16个自由链表中找到合适的链表 result = *my_free_list; if(0 == result){ //没有找到可用的free_list,重新填充free_list void *r = refill(ROUND_UP(n)); return r; } *my_free_list = result->free_list_link; //将result所指向的free_list抽出,将my_free_list指向rsult所指的下一个free_list空间 return result; } static void dealocate(void *p, size_t n) //就是将被释放的free_list头插入相对应的free_list区块 { obj *q = (obj *)p; obj * volatile *my_free_list; if(n > __MAX_BYTES){ malloc_alloc::deallocate(p, n); return ; } //找到对应的free_list my_free_list = free_list + FREELIST_INDEX(n); //调整free_list,回收区块(即头插如对应的free_list) q->free_list_link = *my_free_list; *my_free_list = q; } }; //初始化 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}; //填充函数refill 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; //若只得到一个区块,直接分配给调用者用,free_list无新节点 if(1 == nobjs){ return chunk; } //否则调整free_list, 纳入新的节点 my_free_list = free_list + FREELIST_INDEX(n); //在chunk(内存池)空间内建立新的free_list,并依次将其链接起来 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; //到达最后一个区块,其link为空 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; } //配置heap,用来补充内存池 start_free = (char *)malloc(bytes_to_get); if(0 == start_free){ int i; obj* volatile *my_free_list, *p; //若heap空间不足,就试着在size大小后的区块中查找未用的区块足够大的free_list for(i = size; i <= __MAX_BYTES; i += __ALIGN){ my_free_list = free_list + FREELIST_INDEX(i); p = *my_free_list; if(0 != p){ //free_list中有未用的空间,则调整free_list,释放出空间 *my_free_list = p->free_list_link; start_free = (char *)p; end_free = start_free + i; return chunk_alloc(size, nobjs); //为修正nobjs } } end_free = 0; //即就是heap,和 区块中都没有内存了,就调用一级配置器 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); } } ////////////////////////////////////////////////////////////////////////////// #ifdef __USE_MALLOC typedef __malloc_alloc_template<0> malloc_alloc; typedef malloc_alloc alloc; #else typedef __default_alloc_template<0, 0> alloc; #endif ////////////////////////////////////////////////////////////////////////////// #endif
测试代码如下:
int main() { int *p = (int*)alloc::allocate(sizeof(int)); //int *p1 = (int*)alloc::allocate(sizeof(int)); //alloc::deallocate(p,sizeof(int)); /* int* pa[19]; for(int i=0; i<19; ++i) { pa[i] = (int*)alloc::allocate(sizeof(int)); } //int *p1 = (int *)alloc::allocate(sizeof(int)*10); //int *p2 = (int*)alloc::allocate(sizeof(int)*10); //int *p3 = (int*)alloc::allocate(sizeof(int)*10); //int *p4 = (int*)alloc::allocate(96); //int *p5 = (int*)alloc::allocate(72); //alloc::deallocate(p,4); */ return 0; }
相关文章推荐
- Oracle 11g笔记——调整表空间和数据文件的大小、移动数据文件、联机重做日志文件、控制文件
- 转:svn 更新指定文件夹
- jQuery点击图片弹出放大可拖动图片查看
- nity3D自动打包ipa
- Linux Ethernet Over Usb
- Linux配置查询
- BSON 1.0版本规范(翻译)
- 【并发编程】Future和FutureTask以及CompletionService
- UIViewController数据刷新
- 【BZOJ1324】Exca王者之剑【最小割】
- JavaScript面向对象精要(一)
- hdu2844 Coins --多重背包
- React-Native项目运行时报错:Application xxxProject has not been registered.
- VM虚拟机的配置文件(.vmx)损坏修复
- C++模板的坑
- linux上内存泄漏查看方法之——让系统调用自己定义的new
- Find Minimum in Rotated Sorted Array II
- vtk实战(七)—读取三维点坐标
- 大学生录取预测——逻辑回归
- Idea15 常用设置(一):JDK、SVN