#ifndef _STLALLOC_H_
#define _STLALLOC_H_
#include <typeinfo>
#include <new> //placement new
#include <iostream>
#include <malloc.h>
//#include <>
#include <stdlib.h>
using namespace std;
#if 0 //为何如此定义?
# include <new>
# define __THROW_BAD_ALLOC_
#elif !defined(__THROW_BAD_ALLOC_)
# 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_remalloc(void *,size_t);
static void (*__malloc_oom_handler)(); //void(*)() malloc_oom_handler 想当于定义了一个函数指针
public:
static void *allocate(size_t n)
{
void *result = malloc(n);//一级配置器直接使用malloc(),如果无法满足需求,则调用oom_malloc()(循环)
if(0== result) result = oom_malloc(n);
return result;
}
//一级配置器直接使用free
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_remalloc(p, new_sz);
return result;
}
//此做法类似于C++的set_new_handler处理机制,f代表客户可以自己定义内存不足的处理函数set_new_handler是一个函数,函数的参数为函数指针(void(*)()),返回值为函数指针(void(*)())
static void(*set_malloc_handler(void(*f)()))()
{
void(*old)() = __malloc_oom_handler; //用一个函数指针先保存先前的内存处理函数
__malloc_oom_handler = f; //让私有成员的数据指针指向f(传进来的函数名)即client自己定义的内存不足函数处理函数
return (old);
}
//此函数的作用:(1)函数参数为f 如果客户没有定义自己的处理函数,即此时系统默认的f == NULL;返回值为空,则在循环体中就会调用系统的默认的处理机制(__THROW_BAD_ALLOC_)
(2)此函数返回注册的前异常处理函数,以便前面的处理函数值后还有可能返回;
};
template <int inst>
void(*__malloc_alloc_template<inst>:: __malloc_oom_handler)() = 0;//内存不足处理函数的初值设置为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_oom_handler;//指针指向一个中间桥梁的函数指针(类的私有成员)
if(0 == my_malloc_handler) //即如果客户没有设置自己的内存不足处理函数,系统就会调用自身的处理函数
{
__THROW_BAD_ALLOC_;
}
(*my_malloc_handler)();//如果有,则执行内存客户自定义的内存不足处理函数
result = oom_malloc(n);
if(result) return result;
}
}
template<int inst>
void*__malloc_alloc_template<inst>::oom_remalloc(void *p , size_t n)
{
void(*my_malloc_handler)();
void *result;
for(;;)
{
my_malloc_handler = __malloc_oom_handler;
if(0 == my_malloc_handler)
{
__THROW_BAD_ALLOC_;
}
(*my_malloc_handler)();
result = oom_remalloc(p,n);
if(result) return result;
}
}
//**************************************************************
//**************************************************************
enum{__ALIGN = 8};//小型区块的上调边界(align 匹配,结盟)
enum{__MAX_BYTES = 128};//最大字节
enum{__NFREELISTS = __MAX_BYTES/__ALIGN};//free lists 的个数
//二级配置器无模板参数,且其类型没有作用
template<int inst>
class __default_alloc_template
{
private:
//将字节上调至8的倍数(round up 集成整数)
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];
};
//此设计极为巧妙,对空间的怜惜可见一斑,对空间的分配极为吝啬,结构体占四个字节,当申请到空间时,将申请到的一部分[刚开始(19个)最为自由链表,一个返回,给调用的对象用,剩余的20个作为内存池]用这种形式的节点指针来维护链表,此结构形式极其象柔性数组,相当于用联合体的结构形式,把一个数组一部分链接起来,当第二次还有对象要用此节点时,此时,相当于链表的Popfront先用一个临时指针指向第一块内存,然后链表头指向它的下一块空间,最后将此临时指针返回,供调用对象使用.
//****************************************************************************
private:
//16个自由链表
static obj *volatile free_list[__NFREELISTS];
//volatile 是一个指令关键字,确保本条指令不会因为不会被编译器的优化而省略,且要求每次直接读值
//****************************************************************************
//根据区块的大小定位使用第几号自由链表
static size_t FREELIST_INDEX(size_t bytes)
{
return (((bytes) + __ALIGN -1)/__ALIGN - 1);
}
//****************************************************************************
static void *refill(size_t n);
//返回大小为n 的区块,并可能加入大小为n 的其他区块到freelist.refill()重新填充自由链表,比如刚开始16个自由链表都为0,加入定位了5 个字节,则根据ROUND_UP()则上调至8个字节,然后根据定位函数FREELIST_INDEX()定位到0,定位到0号链表,但刚开始,0 号链表为空,此时要refill(),填充规则刚开始一个自由链表都没有u,则调用chunk_alloc() 函数此时默认申请的大小2 * size(8) * 20 + 一个变化的数(可以不用考虑)。== 320 bytes 将一个返回,19个链成自由链表,剩下的20个作为内存池,
//****************************************************************************
static char *chunk_alloc(size_t size, int& nobjs);
//配置一块大空间,可容纳nobjs个大小为“size”的区块,如果配置nobjs 个大小的区块有所不便,nobjs的大小可能会降低,注意此处是引用,上面函数出来后,可能会改变nobjs的实际大小。
//****************************************************************************
static char *start_free;
static char *end_free;
static size_t heap_size;
//块的分配状态:用start_freeend_free 表示内存池的空间剩余状况,两者都是指针,相当于一个指向数组的没有存数据的起始,一个指向末尾。heap_size 主要是负责refill()函数中开辟空间时的变化的基数,注意此处的三个指针都声明为静态的原因?
public:
static void *allocate(size_t n); //
static void deallocate(void *p, size_t n); //空间的释放
static void *reallocate(void *p, size_t old_size,size_t new_sz);//
};
//****************************************************************************
//以下是类中的静态数据成员的定义与初始值设置
template<int inst>
char*__default_alloc_template<inst>::start_free = 0;
template<int inst>
char*__default_alloc_template<inst>::end_free = 0;
template<int inst>
size_t __default_alloc_template<inst>::heap_size= 0;
template<int inst>
typename__default_alloc_template<inst>::obj *volatile //整行都是返回值,因为obj是在一个不同参数的返回值,所以要声明其解析符
__default_alloc_template<inst>::free_list[__NFREELISTS]=
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //初始16个自由链表,其内部的指向都为空
//****************************************************************************
template<int inst>
void*__default_alloc_template<inst>::allocate(size_t n) //此处n必须必须大于0
{
obj * volatile *my_free_list;
//cout << "size_t : " << n << endl;
obj *result; //定义此指针的目的是指向申请到的,要返回的空间,并将其返回如果申请的字节大于128字节,此时就调用一级空间配置器
if(n > (size_t)__MAX_BYTES)
{
return(__malloc_alloc_template<0>::allocate(n));
}
//指针指向16个链表中合适的一个
my_free_list = free_list + FREELIST_INDEX(n);//此时的my_free_list是二级指针,定位的是下标.
result = *my_free_list;
if(0 == result)//如果定位到的链表为空,则填充申请空间,挂载链表,返回申请到的空间
{
void * r = refill(ROUND_UP(n));
return r;
}
*my_free_list = result->free_list_link;//如果定位的链表有空间,此时重新Popfront第一和节点
return (result); //并返回。
}
//allocate()是二级空间配置器__default_malloc_template 的标准接口,此函数的作用是:首先先判断区块的大小,如果大于128则调用一级配置器,否则就检查对应对自由链表,如果自由链表有可用的区块,则就直接拿来用,如果没有区块,则将区块的大小上调至8的倍数,然后调用refill(),准备为free list重新填充。
//****************************************************************************
template<int inst>
//注意此处的n 是为二级空间配置器的释放所设计的,当大于128 bytes时,直接调用一级空间配置器的释放函数free(p),将对象释放掉,如果小于128 字节,则就不是‘释放’,而是先传入指针所指的对象,并且传入要‘释放’的块的大小。这样,最后释放的时候,其实是将传入的指针所指的块挂载到自由链表中。
void__default_alloc_template<inst>::deallocate(void *p ,size_t n)
{
//cout << "3" << endl;
obj *q = (obj*)p;
obj* volatile *my_free_list;
//cout << n << endl;
//如果大于128字节,就交给一级配置器处理
if(n > (size_t)__MAX_BYTES)
{
//cout << "error" << endl;
__malloc_alloc_template<0>::deallocate(p,n);
}
//否则先定位
my_free_list = free_list + FREELIST_INDEX(n);
//将对象的空间pushfront到自由链表中,因为二级空间配置器,并不是释放空间,而是将空间挂载到自由链表上.(后话:当有对象申请时,此时先定为链表所在的下标,然后将链表上的一块空间用一个临时指针指向然后从自有链表释放,返回给对象临时指针所指的内容.)
q->free_list_link = *my_free_list;
*my_free_list = q;
}
//*****************************************************************************
template<int inst>
void*__default_alloc_template<inst>::refill(size_t n)
{
int nobjs = 20; //默认取的20个新的节点区块,给自由链表填充
//调用chunk_alloc(),尝试取的nobjs个区块作为free list 的新节点
//此处注意nobjs 是引用传值
char *chunk = chunk_alloc(n,nobjs);
//cout << "nobjs = " << nobjs << endl;
obj * volatile *my_free_list; //所有函数中的这种定义都是为了定位自由链表的下标
obj *result; //所有的函数中这种定义都是为了给对象返回result指针所指的空间
obj * current_obj, *next_obj; //为了把申请的空间链成自由链表
//如果只获得一个区块,这个区块就分配给调用者使用,啥也不用做,自由链表没有新的节点
if(1 == nobjs)
{
return chunk;
}
//否则,大于等于1个区块,此时要调整自由链表,纳入新的节点
//(1)先定位:
my_free_list = free_list + FREELIST_INDEX(n);
//(2)让result指向申请的块的第一块内存位置,将其准备返还给客户端
result = (obj *)chunk;
//(3)引导除去第一块外chunk的空间,链成自由链表(注意:此处的空间是取自内存池)
*my_free_list = next_obj = (obj *)(chunk + n);
for(int 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<int inst>
char *
__default_alloc_template<inst>::chunk_alloc(size_tsize, 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; //因为要将total_bytes 的空间分配出去(给自由链表填充),所以要调整内存池的剩余空间,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);//需要申请的空间大小此时,对于剩余的残余空间,将其挂载在自身的自由链表上,让其最后有价值(如果堆上申请不到所需的大块内存,此时就返回来遍历一下,比自己'大'的链表,看看其是否还有尚未用的内存块)
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 = 0;
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;
//此free list 尚有未用的区块
if(0 != p)
{
//调整free lists 释放该区块
*my_free_list =p->free_list_link;
start_free = (char *)p;
end_free = start_free + i;
return (chunk_alloc(size,nobjs));
//任何残余的零头,终将被编入适当的free lists 中,备用。
}
}
end_free = 0; //如果出现意外?(到处都没能找到内存可用)
//调用一级配置器,看看out_of_memory机制能否尽力
start_free = (char*)__malloc_alloc_template<0>::allocate(bytes_to_get);
//这会抛出异常(exception),或内存不足的情况
}
heap_size += bytes_to_get;
end_free = start_free + bytes_to_get;
//递归调用自己,为了修改nobjs
return (chunk_alloc(size,nobjs));
}
}
typedef __default_alloc_template<0>alloc;
#endif
//****************************************************************************
//如果之前定义了__USE_MALLOC这个标签,则就把一级空间配置器定义为alloc
#ifdef __USE_MALLOC
typedef __malloc_alloc_template<0>malloc_alloc;
typedef malloc_alloc alloc;
//如果之前没有定义标签,那么就把二级空间配置器定义为alloc
#else
typedef __default_alloc_template<0>alloc;
#endif
//我之前没有定义__USE_MALLOC 这个标签,所以我把二级空间配置器,定义为alloc接口空间分配器,
//SGI 的空间配置器也是这样做的,开放的是二级空间配置器,然后在最外层包装了一个接口
//simple_alloc
//*****************************************************************************
//SGI 全部使用simple_alloc这个接口:其内部的函数都全部转调用一级二级配置器的函数
template<typename T, typename 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)
{
//测试:
//cout << "typeid(T).name()" << typeid(T).name()<< endl; //要查看模板的类型必须包含<typeinfo>
Alloc::deallocate(p,sizeof(T));
}
};
//****************************************************************************