SGI STL分为两级空间配置器:
以下代码展现了alloc中内存的调度
//alloc.h
#ifndef ALLOC_H
#define ALLOC_H
#if 0
# include <new>
# define __THROW_BAD_ALLOC throw bad_alloc
#elif !defined(__THROW_BAD_ALLOC)
# include <iostream>
# define __THROW_BAD_ALLOC cerr << "out of memory" << endl; exit(1)
#endif
//第一级配置器
//非型别参数inst没有起到作用
template <int inst>
class __malloc_alloc_template {
private:
//以下函数用来处理内存不足的情况 oom 即 out of memory
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); //第一级配置器直接使用malloc()
//无法分配时使用oom_malloc()
if(result == 0) {
result = oom_malloc(n);
}
return result;
}
static void* dellocate(void *p, size_t) {
free(p); //第一级配置器使用free()
}
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;
}
//仿真c++的set_new_handler()机制,指定自己的out_memory_handler
//set_malloc_handler函数 函数参数void (*f)()
// 返回类型void (*f)()
static void (*set_malloc_handler(void (*f)()))() {
void (*old)() = __malloc_alloc_oom_handler;
__malloc_alloc_oom_handler = f;
return old;
}
};
//out_of_memory handler初值设为0
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_alloc_handler = __malloc_alloc_oom_handler;
if(my_alloc_handler == 0) {
__THROW_BAD_ALLOC;
}
(*my_malloc_handler)(); //调用处理例程,企图释放内存
result = malloc(n);
if(result != 0) {
return result;
}
}
}
//与oom_malloc类似
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(my_malloc_handler = 0) {
__THROW_BAD_ALLOC;
}
(*my_malloc_handler)();
result = realloc(p, n);
if(result != 0) {
return result;
}
}
}
typedef __malloc_alloc_template<0> malloc_alloc;
//end(第一级配置器)
//第二级配置器
enum {__ALIGN = 8}; //小型区块的上调边界
enum {__MAX_BYTES = 128}; //小型区块的上限
enum {__NFREELISTS = __MAX_BYTES/__ALIGN}; //free_lists的个数
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_lists的节点构造
union obj {
union obj *free_list_link;
char client_data[1];
};
private:
//16个free_lists
static obj * volatile free_list[__NFREELISTS];
//此函数可根据区块的大小,决定使用第n号free_list, n从0开始
static size_t FREELIST_INDEX(size_t bytes) {
return (((bytes) + __ALLIGN-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);
//块的状态
static char *start_free; //内存池起始位置,只在chunk_alloc()中变化
static char *end_free; //内存池结束位置,只在chunk_alloc()中变化
static size_t heap_size;
public:
static void* 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 lists
void *r = refill(ROUND_UP(n));
return r;
}
//调整free_list
*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;
//大于128则调用第一级配置器
if(n > 128) {
malloc_alloc::dellocate(p, n);
return;
}
//寻找对应的free list
my_free_list = free_list + FREELIST_INDEX(n);
//调整free_list,将q安插在区块头部
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>
char * __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};
//返回一个大小为n的对象,并且有时候会为适当的free list增加节点
//n已经被适当上调为8的倍数
template <bool threads, int inst>
void* __default_alloc_template<threads, inst>::refill(size_t n) {
int nobjs = 20;
//调用chunk_alloc(),尝试取得nobjs个区块作为free list的新节点
char *chunk = chunk_alloc(n, nobjs);
obj * volatile * my_free_list;
obj *result;
obj *current_obj, *next_obj;
int i;
//如果只获得一个区块,将这个区块分配给调用者,free list无新节点
if(nobjs == 1) {
return chunk;
}
//否则调整free list,纳入新节点
my_free_list = free_list + FREELIST_INDEX(n);
//以下在得到的块中建立free list
result = (obj*)chunk; //这一块返回给调用者
*my_free_list = next_obj = (obj *)(chunk + n);
//将各个free list节点串接起来
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;
}
//内存池
//nobjs是引用调用的
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) {
//内存池中还有一些零头,先配给适当的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(start_free == 0) {
//heap空间不足,malloc()失败
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(p != 0) { //free_list内尚有未用区块
//调整free list 以释放未用区块
*my_free_list = p->free_list_link;
start_free = (char*)p;
end_free = start_free + i;
//递归调用自己,为了修正nobjs
return chunk_alloc(size, nobjs);
}
}
end_free = 0; //如果出现意外(没有内存可用了)
//调用第一级配置器,看看out_of_memory机制能否起作用,会抛出异常
//或内存不足的情况得到改善
start_free = (char *)malloc_alloc::allocate(bytes_to_get);
}
heap_size += bytes_to_get;
end_free = start_free + bytes_to_get;
//递归调用自己,为了修正nobjs
return chunk_alloc(size, nobjs);
}
}
#endif