Allocate管理的是内存分配。
这里主要考虑的是对于小额区块的内存管理,如果小额区块直接使用new操作符进行分配,第一会造成内存的碎片;第二配置时会造成额外负担;所谓额外负担就是,每次索取一块内存时,都必须向系统分配一块空间,以记录内存的大小。
所以这里采用两级配置器:
第一级配置器就是当要求的内存足够大时,直接使用malloc和free配置和释放内存;多大是足够大?STL中定义当要求得内存大于128byte时,就是足够大了。
//mymemory.h
#ifndef _MYMEMORY
#define _MYMEMORY
#include<new>
#define __THROW_BAD_ALLOC //throw bad_alloc
//第一级配置器
//此处inst完全没有用到,根据Effective C++中记载,这样可以产生不同的实体
//使每个继承此类的class,拥有实体互异的此类复件,主要是static成员变量
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:
/*interface:
*allocate 配置内存
*deallocate 释放内存
*reallocate重置内存
*/
//此处n代表nbyte
static void* allocate(size_t n)
{
void* result =malloc(n);//第一级配置器直接使用malloc
//当无法满足要求时,改用oom_malloc(n)
if(0==result) result=oom_malloc(n);
return result;
}
static void deallocate(void* p,size_t /* n*/)
{
free(p);//第一级配置器直接使用free释放
}
static void* reallocate(void* p,size_t/*old_size*/,size_t new_sz)
{
void* result=realloc(p,new_sz);
//当无法满足要求时,改用oom_realloc
if(0==result) result=oom_realloc(p,new_sz);
return result;
}
//此处没有用new操作符,所以需要仿真出C++new-hander机制
static void (* set_malloc_handler(void(*f)()))()
{
void(*old)()=__malloc_alloc_oom_handler;
__malloc_alloc_oom_handler=f;
return old;
}
};
//初值为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_malloc_handler=__malloc_alloc_oom_handler;
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;
}
}
//使用时,直接将inst指定为0
typedef __malloc_alloc_template<0> malloc_alloc;
//第二级配置器
/*解析:第二级配置器,解决两个问题:一是内存碎片问题,二是配置时的额外负担,因为,每次要求配
*置内存,都要有一个cookie,记录内存大小,所以要求的内存越小,cookie占的比重越大,额外负担
*越大;
*第二级配置器,是默认配置器,当要求的内存超过128byte时,移交到第一级配置器
*当小于128byte时,以内存池管理:每次配置一大块内存,并维护对应得自由链表,下次若再有相同
*大小的内存需求,就直接从free-list中拨出,如果客端释还小额区块,就由配置器回收到free-list中
*其中这些小额区块的内存需求量会被上调至8的倍数
*第二级配置器共有16个free-list,管理大小为8,16,24,32,40,48,56,64,72,80,88,96,104
*112,120,128byte的区块
*/
/*内存分三级结构 free-list<--------内存池<----------堆空间heap
*首先从heap中申请一大块空间,放在内存池中,内存池由始末两个指针划定;
*用户申请的内存区块是从free-list中申请的,当free-list空间不足时,内存池会分配空间给
*free-list,当内存池中空间不足时,从heap分配空间给free-list
*-——————--——————————————————————————————-
*|#0| #1| #2|#3|#4|#5|#6|#7|#8|#9|#10|#11|#12|#13|#14|#15|#16|
*-————————————————————————————————————---
*上为freelist结构,每一个位置都存储一个链表,每条链表中元素都是不同大小的区块
*/
//此处,不知道为什么使用enum。。。。
enum{__ALIGN=8};//小型区块的上调边界
enum{__MAX_BYTES=128};//小型区块的上限
enum{__NFREELISTS=__MAX_BYTES/__ALIGN};//free-lists个数
//忽略多线程
template<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));}//与~(__ALIGN-1)相与导致最小值必然是8
//8的倍数
private:
//free-list的节点结构
union obj
{
union obj* free_list_link;
char client_data[1];
};
private:
//16个free-list,我对volatile不是很熟悉
static obj* volatile free_list[__NFREELISTS];
//根据byte大小,决定使用第几号free-list
static size_t FREELIST_INDEX(size_t bytes)
{ return (((bytes)+__ALIGN-1)/__ALIGN-1);}
//当发现free-list没有可用区块时,重新填充内存
static void* refill(size_t n);
//配置一大块空间,可容纳nobjs个大小为size的区块
static char* chunk_alloc(size_t size,int& nobjs);//是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);
static void deallocate(void* p,size_t n);
static void* reallocate(void* p,size_t old_sz,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
__default_alloc_template<inst>::free_list[__NFREELISTS]=
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};//free_list[i]为一条链表
template<int inst>
void* __default_alloc_template<inst>::allocate(size_t n)
{
obj* volatile * my_free_list;//my_free_list指向链表,链表中存放的是obj*
obj* result;
//大于128 就调用第一级配置器
if(n>(size_t)__MAX_BYTES){
return(malloc_alloc::allocate(n));
}
//寻找16个链表中适当的一个
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;
}
*my_free_list=result->free_list_link;
return (result);
};
template<int inst>
void __default_alloc_template<inst>::deallocate(void* p,size_t n)
{
obj* q=(obj*)p;
obj* volatile * my_free_list;
if(n>(size_t)__MAX_BYTES){
malloc_alloc::deallocate(p,n);
return;
}
my_free_list=free_list+FREELIST_INDEX(n);
//调整free-list,回收区块
q->free_list_link=*my_free_list;
*my_free_list=q;
}
//返回一个大小为n的对象,并且有时候为适当的free-1ist增加节点
//假设n已经上调至8的倍数
template<int inst>
void* __default_alloc_template<inst>::refill(size_t n)
{
int nobjs=20;
//调用chunk_alloc,尝试取得nobjs个区块作为fre-list的新节点
char* chunk=chunk_alloc(n,nobjs);
obj* volatile * my_free_list;
obj* result;
obj* current_obj;
obj* next_obj;
//如果只获得一个区块,这个区块就分配给调用者,free-list无新节点
if(1==nobjs)return(chunk);
//否则准备调整free-list,接纳新节点
my_free_list=free_list+FREELIST_INDEX(n);
result=(obj*)chunk;
*my_free_list=next_obj=(obj*)(chunk+n);
//将free-list的各节点穿起来
for(int 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;
}
//从内存池中取空间给free-list
//假设size已上调至8的倍数,nobjs按引用传递
template<int inst>
char* __default_alloc_template<inst>::chunk_alloc(size_t size,int& nobjs)
{
char* result;
size_t total_byte=size*nobjs;
size_t byte_left=end_free-start_free;//内存池的剩余空间
if(byte_left>total_byte){
result=start_free;
start_free+=total_byte;
return result;
}else if(byte_left>=size){
//此时内存池中剩余空间量不能完全满足需求,但能够供应一个以上区块
nobjs=byte_left/size;
total_byte=size*nobjs;
result=start_free;
start_free+=total_byte;
return result;
}else{
//内存池中连一个区块都无法提供
size_t byte_to_get=2*total_byte+ROUND_UP(heap_size>>4);//
//以下操作试着让内存池中的残余零头还有利用价值
if(byte_left>0){
//分配给适当的free-list
obj* volatile* my_free_list=free_list+FREELIST_INDEX(byte_left);
//调整free-list,容纳新节点
((obj*)start_free)->free_list_link=*my_free_list;
*my_free_list=(obj*)start_free;
}
//配置heap空间。补充内存池
start_free=(char*)malloc(byte_to_get);
if(0==start_free){
//malloc失败
obj* volatile* my_free_list;
obj* p;
//试着检查我们手上的东西,搜寻适当的区块,即:尚未使用,且区块足够大的free-list
for(int i=size;i<=__MAX_BYTES;i+=__ALIGN){
my_free_list=free_list+FREELIST_INDEX(i);
p=*my_free_list;
if(0!=p){
//调整free_list以释放未用区块,将这一区块还给内存池
*my_free_list=p->free_list_link;
start_free=(char*)p;
end_free=start_free+i;
//现在内存池中有足够一个size的空间了
return (chunk_alloc(size,nobjs));
}
}
end_free=0;//这时候确实没有内存可用了
//调用第一级配置器的out_of_memory机制
start_free=(char*)malloc_alloc::allocate(byte_to_get);
//会导致抛出异常或者获得内存
}
heap_size+=byte_to_get;
end_free=start_free+byte_to_get;
//现在内存池中有足够的空间了
return (chunk_alloc(size,nobjs));
}
}
/*第二级配置器必须遵循的契约就是以8的倍数单位的内存分配,例如分配时,100byte,会返回128
*byte,然而回收时,对于100byte,会回收128byte
*/
//第一二级内存配置器的代码已具备,怎么用呢?
//使用时,另包装有一个接口,使得配置单位由byte转化为个别元素的大小
template<typename T,typename Alloc>
class simple_alloc
{
public:
static T* allocate(size_t n)//配置n个T对象
{return 0==n ? 0:(T*)Alloc::allocate(n*sizeof(T));}
static T* allocate(void)//配置一个T对像
{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));}
};
//使用范例
#ifdef __USE_MALLOC
typedef malloc_alloc alloc;//令第一级配置器为默认配置器
#else
typedef __default_alloc_template<0> alloc;//令第二级为默认配置器
#endif
使用范例:
#ifdef __USE_MALLOC
typedef malloc_alloc alloc;//令第一级配置器为默认配置器
#else
typedef __default_alloc_template<0> alloc;//令第二级为默认配置器
#endif
template<class T,class Alloc=alloc>
class vector
{
public:
typedef T value_type;
protected:
typedef simple_alloc<value_type,Alloc> data_allocator;
};
typedef simple_alloc<int,alloc> int_new;
int main()
{
int* s=int_new::allocate(5);
s[0]=0;s[1]=1;s[2]=2;s[3]=3;s[4]=4;
for(int i=0;i<5;++i){
std::cout<<s[i];
}
int_new::deallocate(s,5);
char c=getchar();
}