STL空间配置器——第二级配置器__default_alloc_template剖析
来源:互联网 发布:淘宝商城包包大全 编辑:程序博客网 时间:2024/05/02 03:11
SGI第二级空间配置器较第一级空间配置器加入了内存池(memory pool)管理,即次层配置。当所申请的空间大于128bytes时,直接调用一级空间配置器处理,小于128bytes时,使用次层配置器管理。申请的空间不足8的倍数,默认提升为最近的8的整数倍的空间大小。
开篇
__default_alloc_template
模板类的声明以及一些成员的初始化
//以下是第二级配置器//notice:没有"template"类型参数,第二个参数没有派上用处//第一个参数用于多线程环境下,目前不讨论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: union obj //free_lists节点构造 { union obj * free_list_link; char client_data[1]; };private: //16个free_lists static obj * volatile free_list[__NFREELISTS]; //根据区块大小,决定使用第n号free-list, n从1算 static size_t FREELIST_INDEX(size_t bytes) { return (((bytes) + __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); // chunk allocation state 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 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};
第二级空间配置器的做法
一 当区块足够大,超过128bytes时,直接使用第一级空间配置器进行处理;
if(n > (size_t)__MAX_BYTES){ return malloc_alloc::allocate(n);}
二 当区块小于等于128bytes时,以内存池进行管理;
每次配置一大块内存,维护其对应的自由链表free_lists中16个free_list,每个free_list的union obj串的每一个节点都是8的倍数的内存,链表的结构如
union obj{ union obj * free_list_link; char client_data[1];}//一物两用,保存下一个free_list的指针,也保存实际区块的内容
free_lists的结构如图:
当所allocate的内存小于128bytes,调用allocate
对于其实现,这里分为以下几种情况
1.如果free_list之内有可用区块,就直接拿来用
obj * volatile *my_free_list; obj * result; my_free_list = free_list + FREELIST_INDEX(n); result = *my_free_list; //调整free list *my_free_list = result->free_list_link; return (result);
2.如果没有可用的free_list,则利用refill函数重新填充free list
if(result == 0) { //没找到free_list,准备重新填充free list void *r = refill(ROUND_UP(n)); //refill为free_list重新填充空间,新的空间取自内存池(由chunk_alloc()完成) return r; }
以下是完整代码阐述
template<bool threads, int inst>void * __default_alloc_template<threads, inst>::allocate(size_t n){ obj * volatile *my_free_list; obj * result; //大于128字节就调用第一级配置器 if(n > (size_t)__MAX_BYTES) { return malloc_alloc::allocate(n); } //寻找16个free lists中适当的一个 my_free_list = free_list + FREELIST_INDEX(n); result = *my_free_list; if(result == 0) { //没找到free_list,准备重新填充free list void *r = refill(ROUND_UP(n)); return r; } //调整free list *my_free_list = result->free_list_link; return (result);}
以上是第二级空间配置器的总体解决办法,以下对refill()以及很关键的chunk_alloc()进行阐述。
三 refill()重新填充free lists
allocate获取可用区块,如果没有可用的free_lists,就要调用refill()填充空间,新的空间取自内存池(由chunk_alloc()完成),nobjs为20,以引用形式传入chunk_alloc即缺省取得20个新节点(新区块),当内存池空间不足,最终nobjs可能小于20.
refill函数做了两件事,一是利用chunk_alloc申请空间,二是将申请的空间第一个块传给客端,其他19(甚至更少)个块串起来
refill()实现是这样的:
1.调用chunk_alloc尝试获取20个可用区块
int nobjs = 20; //调用chunk_alloc(),尝试取得nobjs个区块作为free_list的新节点 //注意参数nobjs是pass by reference char * chunk = chunk_alloc(n, nobjs);
2.如果可用区块只有一个,直接返回给客端
obj * volatile * my_free_list; obj * result; obj * current_obj, * next_obj; int i; //如果只获得一个区块,这个区块就分配给调用者,free_list无新节点 if(1 == nobjs) return (chunk);
3.如果可用区块不只一个,调整free_list,纳入新节点
//my_free_list指向自己在free_lists中的位置 my_free_list = free_list + FREELIST_INDEX(n); //以下在chunk空间内建立free list result = (obj *)chunk; //这一块准备返回给客端 //以下导引free_list指向新配置的空间(取自内存池) *my_free_list = next_obj = (obj *)(chunk + n); //以下将free_list的各节点串接起来 for(i = 1; ; i++) { //从1开始,因为第0个将返回给客端 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);
4.如果chunk_alloc()申请不到空间,chunk_alloc()会抛出异常,这个异常是由一级空间配置器oom机制抛出的,下面再叙述
四 chunk_alloc()从内存池中取空间给free_list
chunk_alloc()函数用end_free-start_free
,来判断内存池剩余的水量
char * result; size_t total_bytes = size * nobjs; size_t bytes_left = end_free - start_free; //内存池剩余空间
分为以下几种情况
1.如果bytes_left满足需求量(即20个区块),直接调出
if(bytes_left >= total_bytes) { //内存池剩余空间完全满足需求量 result = start_free; start_free += total_bytes; return (result); }
2.如果水量不足以提供20个区块,但足够提供一个及以上的区块,就把这段空间调出。这时,nobjs将被修改为实际能够提供的区块数
else if(bytes_left >= size) { //内存池剩余空间不能完全满足需求量,但足够供应一个含以上区块 nobjs = bytes_left / size; total_bytes = size * nobjs; result = start_free; start_free += total_bytes; return (result); }
3.如果内存池连一个区块都无法给客端提供,就调用malloc申请配置内存,新申请的空间是需求量的两倍与随着配置次数增加的附加量(即nobjs*size*2+附加量);在申请之前,将内存池中残馀的零头回收
size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4); //以下试着让内存池的残馀零头还有利用价值 if(bytes_left > 0) { //内存池还有一些零头,先配置给适当的free_list //首先寻找适当的free_list obj * volatile * my_free_list = free_list + FREELIST_INDEX(bytes_left); //调整free_list,将内存池中的残馀空间编入 ((obj*)start_free)->free_list_link = *my_free_list; *my_free_list = (obj*)start_free; } //配置heap空间,用来补充内存池 start_free = (char*)malloc(bytes_to_get);
当然也存在申请不成功的情况
此时如果“尚有可用区块,且区块足够大”,则调整free_list释出未用区块,这些残馀零头编入自己的free_list,调整nobjs
int i; obj *volatile * my_free_list, *p; //搜索适当的fee_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)); } } end_free = 0;
如果没有,那就山穷水尽,调用一级配置器,看oom机制能否帮个忙,这样会产生两种可能抛出bad_alloc异常or内存不足的情况得到改善
start_free = (char*)malloc_alloc::allocate(bytes_to_get);
这几种情况,最后都需要调整内存池的始终start_free,end_free,以及heap_size,递归调用自身调整nobjs
heap_size += bytes_to_get; end_free = start_free + bytes_to_get; return (chunk_alloc(size, nobjs));
如图:内存池实际操作
以下是chunk_alloc的完整代码
//假设size已经适当上调至8的倍数//注意参数nobjs是pass bu referencetemplate <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) { //内存池还有一些零头,先配置给适当的free_list //首先寻找适当的free_list obj * volatile * my_free_list = free_list + FREELIST_INDEX(bytes_left); //调整free_list,将内存池中的残馀空间编入 ((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) { //heap空间不足,malloc()失败 int i; obj *volatile * my_free_list, *p; //搜索适当的fee_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)); } } 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)); }}
阅读全文
1 0
- STL空间配置器——第二级配置器__default_alloc_template剖析
- SGI STL第二级空间配置器__default_alloc_template的chunk_alloc函数
- STL空间配置器-第二级配置器
- STL源码分析--第二级空间配置器
- SGI STL空间配置器-第二级空间配置器
- 【深度探索STL】空间配置器(三) 第二级配置器
- SGI STL第二级空间配置器空间释放函数deallocate
- SGI STL 第二级空间配置器函数 allocate()
- SGI STL 第二级空间配置器 内存池
- 【STL深入学习】SGI STL空间配置器详解(二)-第二级空间配置器
- STL剖析——空间配置器
- 第二级配置器 _ _default_alloc_template 剖析
- SGI STL空间配置器详解(二)-第二级空间配置器
- 深入学习STL第二级配置器
- STL-空间配置器剖析
- 剖析STL空间配置器
- 《STL源码剖析》STL空间配置器
- STL源码剖析——空间配置器和迭代器
- pixi.js给附加的canvas加个id
- 用canvas 制作一个冒泡效果
- 半夜的噩梦
- js获取音频条 频谱的动画数据 每秒频谱数值
- html canvas js音频条动画源码 仿电喵的视频风的频谱
- STL空间配置器——第二级配置器__default_alloc_template剖析
- [struts]jsp获取数据
- 简单线性回归
- getParameter 和getAttribute的区别
- 网络传输介质
- 设计模式笔记之工厂模式
- 问题Jpa 返回别名,一个结果集取值
- MyBatis Map结果的Key转为驼峰式2
- cloudera安装--获取锁问题