Allocate--摘自STL源码剖析

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();

}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值