众所周知,STL中的空间配置器分为一级空间配置器和二级配置器。第一级配置器以malloc( ),free(),realloc()等C函数执行实际的内存配置、释放、重配置操作,并实际出类似C++ new-handler的机制。
C++ new handler机制是,你可以要求系统在内存配置需求无法被满足时,调用一个你所指定的函数。换句话说,一旦::operator new 无法完成任务,在丢出std::bad_alloc异常状态之前,会先调用由客户指定的处理例程。该处理例程通常即被称为new-handler。
SGI第一级配置器的allocate()和 realloc()都是在调用malloc()和realloc()不成功后,改调用oom_malloc()和oom_realloc()。后两者都有内循环,不断调用“内存不足处理机制”,期望在某次调用之后,获得足够的内存而圆满完成任务。但如果“内存不足处理例程”并未被客户设定,oom_malloc()和oom_realloc()便毫不客气地调用_THROW_BAD_ALLOC,丢出bad_alloc异常信息,或利用exit(1)硬生生中止程序。
下面给出具体的代码:
#ifndef _STL_ALLOC_H_
#define _STL_ALLOC_H_
#if 1
#include<iostream>
#include<malloc.h>
#include<exception>
#include<stdlib.h>
#include<string.h>
using namespace std;
#define __THROW_BAD_ALLOC cerr<<"Out Of Memery."<<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(result == 0)
{
//第一次申请不成功再次申请
result = oom_malloc(n);
}
return result;
}
//空间回收函数
static void deallocate(void* p,size_t n)
{
free(p);
}
static void* reallocate(void*p,size_t,size_t new_sz)
{
void *result = realloc(p,new_sz);
if(result == 0){
result = oom_realloc(p,new_sz);
}
return result;
}
//set_malloc_handler为一个函数,参数为一个函数指针
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;
//如果my_malloc_handler为0,说明用户没有设置内存不足时的处理函数
//抛出异常
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;
}
}
}
下面我们来看二级空间配置器:
SGI第二级配置器的做法是,如果区域够大,超过128bytes时,就要移交给第一级配置器处理。当区块小于128bytes时,则以内存池管理,此法又称为次层配置:每次配置一大块内存,并维护对应之自由链表(free-list)。下次若再有相同大小的内存需求,就直接从free-list中拨出,如果客端释还小额区块,就由配置器回收到free-list中。为了方便管理,SGI第二级配置器主动将任何小额区块的内存需求量上调8的倍数,并维护16个free-list。
typedef __malloc_alloc_template<0> malloc_alloc;
///////////////////////////////////////////////////////////////////////////////
//二级空间配置器
enum{__ALIGN = 8}; //小型区块的上调边界
enum{__MAX_BYTES = 128}; //小型区块的上界
enum{__NFREELISTS = __MAX_BYTES / __ALIGN}; //free-list个数
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:
//free-list的节点构造
union obj
{
union obj * free_list_link;
char client_data[1];
};
private:
//16个free-list
static obj * volatile free_list[__NFREELISTS];
//以下的函数根据区块的大小,决定使用第n号的free-list,n从0开始算
static size_t FREELIST_INDEX(size_t bytes)
{
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;
//当n > 128 时,转到一级空间配置器
if(n > __MAX_BYTES)
{
return malloc_alloc::allocate(n);
}
//根据n的值,索引适合的小标
my_free_list = free_list + FREELIST_INDEX(n);
result = *my_free_list;
//当free-list为空时,准备重新填充free-list
if(result == 0){
void *r = refill(ROUND_UP(n));
return r;
}
*my_free_list = result->free_list_link;
return result;
}
static void deallocate(void *p,size_t n)
{
obj *q = (obj*)p;
obj * volatile * my_free_list;
if(n > __MAX_BYTES){
malloc_alloc::deallocate(p,n);
return ;
}
my_free_list = free_list + FREELIST_INDEX(n);
q->free_list_link = *my_free_list;
*my_free_list = q;
}
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};
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;
if(1 == nobjs){
return chunk;
}
my_free_list = free_list + FREELIST_INDEX(n);
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;
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;
}
//通过malloc申请比较大的空间
start_free = (char*)malloc(bytes_to_get);
//当申请失败时,
if(0 == start_free){
//在自由链表中寻找空间
int i ;
obj * volatile * 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;
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);
}
}
template<bool threads,int inst>
void* __default_alloc_template<threads,inst>::reallocate(void *p,size_t old_sz,size_t new_sz)
{
void * result;
size_t copy_sz;
if(old_sz > (size_t) __MAX_BYTES && new_sz > (size_t)__MAX_BYTES){
return (realloc(p,new_sz));
}
if(ROUND_UP(old_sz) == ROUND_UP(new_sz)) return(p);
result = allocate(new_sz);
copy_sz = new_sz > old_sz ? old_sz : new_sz;
memcpy(result,p,copy_sz);
deallocate(p,old_sz);
return (result);
}
/////////////////////////////////////////////////////////////////////////////////////
//
#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
下面给出一个大概的示意图: