SGI版本的空间配置器解析
SGI 版本一级空间配置器剖析
具备次配置力的,空间利用效率很高,能够有效利用碎片化的空间。设置了两个层级的空间配置器,一级空间配置器用于申请>128,二级空间配置器用于申请<128。
C++处理申请空间失败的机制:
set_new_handler(函数指针),当程序内存申请失败的时候则会执行set_new_handler函数,看它能不能释放一些内存。
#ifndef _STL_ALLOC_H_
#define _STL_ALLOC_H_
#if 0
#include<new>
#define __THROW_BAD_ALLOC throw bad_alloc
#elif !defined (__THROW_BAD_ALLOC)
#include<iostream.h>
#include<malloc.h>
#include<stdlib.h>
#define __THROW_BAD_ALLOC cerr<<"Out Of Memory."<<endl; exit(1)
#endif
//一级空间配置器的实现
template<int inst>
class __malloc_alloc_template
{
public:
//申请空间
static void* allocate(size_t n)
{
void *result = malloc(n);//第一级配置直接使用malloc()
//以下无法满足需求时,改用oom_malloc()
if(0 == result)
result = oom_malloc(n);
return result;
}
static void deallocate(void *p, size_t)
{
free(p);//第一级配置器直接使用free()
}
static void reallocate(void *p, size_t old_sz, size_t new_sz)
{
void *result = realloc(p, new_sz);//第一级配置器直接使用realloc()
//以下无法满足需求时,改用oom_realloc()
if(0 == result)
result = oom_realloc(p, new_sz);
return result;
}
public:
//以下仿真C++的set_malloc_handler()
//可以通过它指定你自己的out-of-memory handler
static void(* set_malloc_handler(void(*f)()))()
{
void(*old)() = __malloc_alloc_oom_handler;
__malloc_alloc_oom_handler = f;
return old;
}
private:
//以下都是函数指针,所代表的函数将用来处理内存不足的情况
//oom:out of memory
static void* 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;
}
}
static void* oom_realloc(void *p, size_t new_sz)
{
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);
}
}
static void(*__malloc_alloc_oom_handler)();//函数指针
};
//静态成员的初始化,放到类外来初始化
//初始值客户端为0,有待客户端设置
template<int inst>
void(*__malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;
//注意,以下参数直接将参数inst 指定为0
typedef __malloc_alloc_template<0> malloc_alloc;
- 第一级配置器以malloc(),free(),realloc()等C函数执行实际的内存配置、释放、重配置操作,并出现类似C++new-handler的机制。他不能直接运用C++ new-handler的机制,因为它并非使用::operator new来配置内存。
- 内存配置无法满足的时候,你可以调用一个指定的函数。::operator new无法完成任务的时候,在丢出异常状态之前,会先调用客端指定的处理例程。
- 底层用的C函数, 可能是历史原因
- SGI的第一级配置器的allocate()和realloc()都是在调用malloc()和realloc()不成功后,改调用oom_malloc()和oom_realloc()。后者都有内循环,不断调用“内存不足处理例程”,希望在某次调用之后,获得足够的内存而圆满完成任务。如果没有设定内存不足处理例程,那么就会调用__THROW_BAD_ALLOC,抛出"Out Of Memory."。
SGI 版本二级空间配置器剖析
#ifndef _STL_ALLOC_H_
#define _STL_ALLOC_H_
#if 0
#include<new>
#define __THROW_BAD_ALLOC throw bad_alloc
#elif !defined (__THROW_BAD_ALLOC)
#include<iostream.h>
#include<malloc.h>
#include<stdlib.h>
#define __THROW_BAD_ALLOC cerr<<"Out Of Memory."<<endl; exit(1)
#endif
//一级空间配置器的实现
template<int inst>
class __malloc_alloc_template
{
public:
//申请空间
static void* allocate(size_t n)
{
void *result = malloc(n);
if(0 == result)
result = oom_malloc(n);//out of memory前提下,调用的一个补救函数
return result;
}
static void deallocate(void *p, size_t)
{
free(p);
}
static void reallocate(void *p, size_t old_sz, size_t new_sz)
{
void *result = realloc(p, new_sz);
if(0 == result)
result = oom_realloc(p, new_sz);
return result;
}
public:
//函数名,返回的是无返回值、无参数的函数指针
//等效于static void()* set_malloc_handler(void(*f)())
static void(* set_malloc_handler(void(*f)()))()
{
void(*old)() = __malloc_alloc_oom_handler;
__malloc_alloc_oom_handler = f;
return old;
}
private:
static void* 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;
}
}
static void* oom_realloc(void *p, size_t new_sz)
{
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);
}
}
static void(*__malloc_alloc_oom_handler)();//函数指针成员
};
//静态成员的初始化,放到类外来初始化
template<int inst>
void(*__malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;
typedef __malloc_alloc_template<0> malloc_alloc;//给类名重新起名
//
//用枚举来定义,相比于宏它会做类型检查
enum {__ALIGN = 8};
enum {__MAX_BYTES = 128};
enum {__NFREELISTS = __MAX_BYTES / __ALIGN};
template<bool threads, int inst>
class __default_alloc_template
{
public:
static void* allocate(size_t n)
{
if(n > __MAX_BYTES)//大于128
{
return malloc_alloc::allocate(n);//调用一级空间配置器
}
obj **my_free_list;
obj *result;
my_free_list = free_list + FREELIST_INDEX(n);
result = *my_free_list;
if(result == 0)
{
void *r = refill(ROUND_UP(n));
return r;
}
//如果有空间的话,my_free_list移动到链表的下一端,返回result
*my_free_list = result->free_list_link;//更改*my_free_list的指向
return result;
}
static void deallocate(void *p, size_t n);
static void* reallocate(void *p, size_t old_sz, size_t new_sz);
protected:
static void* refill(size_t n);
static char* chunk_alloc(size_t size, int &nobjs);
private:
//将byte上升为__ALIGN的倍数
//该方法只能够上升为2的幂次
static size_t ROUND_UP(size_t bytes)
{
return ((bytes + __ALIGN-1) & ~(__ALIGN-1));
}
//核心
union obj
{
union obj *free_list_link;
char client_data[1];
};
static size_t FREELIST_INDEX(size_t bytes)
{
return ((bytes + __ALIGN-1)/__ALIGN-1);//先除,再来减一
}
private:
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>
size_t __default_alloc_template<threads, inst>::heap_size = 0;
//类型是obj*
template<bool threads, int inst>
__default_alloc_template<threads, inst>::obj*
__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);//此处的nobjs是个引用,从空间中拿了20个8出来
obj ** my_free_list;
obj *result;
obj *current_obj, *next_obj;
//如果只能获得一个区块,这个区块就分配给调用者用,free list 无新节点
if(nobjs == 1)
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);//+n因为是一块一块(都是8的倍数)的内存
//以下将free list的各个节点串接起来
for(int i=1; ; i++)//将剩下的19个形成free list
{
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;//将申请的空间一分为二(拿160的空间到底干什么呢?),start_free和end_free之间维持的是内存池的水量
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);//为什么要加整个空间的1/16???减少malloc的次数,实际上是一个经验值
//整体只会形成一个内存池,前面用不完,后面就不会生成
if(bytes_left > 0)//在申请新的空间之前,处理剩余的空间,找到大小一样的free list做一个头插
{
obj **my_free_list = free_list + FREELIST_INDEX(bytes_left);
((obj*)start_free)->free_list_link = *my_free_list;
*my_free_list = (obj*)start_free;
}
start_free = (char*)malloc(bytes_to_get);//第一次从这里申请到空间之后
if(0 == start_free)
{
int i;
obj ** 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; // In case of exception.
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
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 /* _STL_ALLOC_H_ */
PS: 二级空间配置器还是比较复杂的,最好可以把代码放到IDE中运行一下,然后去一步一步追踪过程,这样才能够更加深刻的理解其过程。
volatile关键字
告诉编译器,参与运算的时候,不要直接从寄存器取值(而是每次都要从内存中加载),防止内存中的值已经变化而发生错误。一般在嵌入式编程中比较常见。
内存池技术的结构以及头插的过程示意图