STL相关知识之空间配置器底层实现原理

//我们知道空间配置器在实现的时候其实是封装了一层malloc,如果定义了__USE_MALLOC宏就用一级空间配置器,
//没有的话就是二级空间配置器.

///
//一级空间配置器(内存大于128b)
//1、申请空间,利用allocate,释放空间使用deallocate
//2、申请空间失败调用异常处理函数


//二级空间配置器就是负责小块内存的分配
template<int inst>
class __DefaultAllocTemplate
{
	//二级空间配置器中首先包含一个内存池,其次就是一个自由链表
public:
	static void* Allocate(size_t bytes)
	{
		if (bytes > (size_t)_MAX_BYTES)
		{
			//这是内存池中空间数量的最大值,所以必须向一级空间配置器申请

		}
		//先在自由链表中找出对应bytes的位置,看此位置下面有没有挂空间
		size_t index = FREE_LIST_INDEX(bytes);
		Obj* result;
		result = _free_list[index];
		if (result == 0){
			//对应位置没有空间,这是就应该去内存池中申请
			//bytes个空间,但是注意拿下来应该也要是8的倍数
			void* res = Refill(ROUND_UP(bytes);
		}
		//拿走一个bytes大小的空间,相当于头删
		_free_list[index] = result->free_list_link;
		return result;
	}
	size_t FREE_LIST_INDEX(size_t bytes){
		//算出Bytes在自由链表中映射的下标
		return (bytes + _ALIGIN - 1) / _ALIGIN - 1;
	}
	size_t ROUND_UP(size_t bytes){
		//计算给定的bytes取整到_ALIGIN的整数倍,此处_ALIGIN是8,所以处理成8的倍数就是保证处理
		//后的第三位都为0,其他任意就都可以被8整除
		return (bytes + _ALIGIN - 1)&~(_ALIGIN - 1);
	}
	static void* Refill(size_t bytes)
	{
		//此时系统默认一次取出20个对象的空间(这是因为一些容器在插入元素时就会增容申请空间,
		//本来他们的这种操作就是线程不安全的,所以就要尽量的避免频繁的去取,假如一个正在insert申请空间,
		//另外一个也要申请空间就会产生死锁)
		size_t nobjs = 20;
		char* chunk = ChunkAllocate(bytes, nobjs);
		if (nobjs == 1){
			//如果只有一个那就直接返回
			return chunk;
		}
		//20个对象成功申请到,就返回一个,将剩余的19个直接挂在自由链表的对应位置处
		size_t index = FREE_LIST_INDEX(bytes);
		Obj** myfreelist = _free_list + index;
		//本来一个格子里面就是一个Obj*,因为要头插用一个二级指针来保存当前格子的地址。
		Obj* result = (Obj*)chunk;//返回的空间的地址
		Obj* next_obj = (Obj*)(chunk + bytes);
		Obj* cur_obj;
		for (size_t i = 0; i < 19; i++){
			cur_obj = next_obj;
			next_obj = (Obj*)((char*)(next_obj + bytes));
			cur_obj->free_list_link = next_obj;
		}
		cur_obj->free_list_link = 0;
		return result;
	}
	//bytes:一个对象的大小,nobjs:对象的个数
	static char* ChunkAllocate(size_t bytes, size_t nobjs)
	{
		//本来是想要20个对象的空间,此处处理的时候如果够20个就拿走20个,
		//如果不够再看够不够至少一个对象的,假如向够的话就返回一个对象,
		//连一个对象也分配不出来了此时有两种解决方法
		//1、直接向系统申请
		//2、在自由链表中index后面的位置去找
		size_t totalbytes = bytes*nobjs;
		size_t bytesleft = _end_free - _start_free;
		if (bytesleft > totalbytes){
			//说明空间足以分配出20个对象。
			return _start_free;
			_start_free += totalbytes;

		}
		//不足以分配20个对象,但起码够一个对象,有多少拿多少
		if (bytesleft >= bytes){
			nobjs = (_end_free - _start_free) / bytes;
			_start_free += nobjs*bytes;
			return _start_free;
		}
		//此时内存池已经没有办法分配出一个对象的空间了,
		//此时要做两件事首先将内存池中剩余的空间挂到自由链表上,避免了内存碎片
		if (bytesleft > 0){
			Obj**  myfreelist = _free_list + FREE_LIST_INDEX(bytesleft);
			((Obj*)_start_free)->free_list_link = *myfreelist;
			*myfreelist = (Obj*)_start_free
		}
		//其次就是重新向系统申请内存,这时就要求申请2*totalbytes再多一点(8个字节)相当于40倍的bytes
		size_t bytes_to_get = 2 * totalbytes + ROUND_UP(heap_size >> 4);
		_start_free = (char*)malloc(bytes_to_get);
		if (_start_free == NULL){
			//说明向堆申请空间的时候也失败了,尝试向自由链表Bytes对应位置后面的空间
			Obj**  myfreelist = _free_list + FREE_LIST_INDEX(bytes);
			Obj* p;
			for (int i = index + 1; i < 15; i++){
				myfreelist = _free_list + i;
				p = *myfreelist;
				if (p != NULL){
					*myfreelist = p->free_list_link;
					_start_free = (char)* p;
					_end_free = _start_free + (i+1)*_ALIGIN;
					return (ChunkAllocate(bytes, nobjs));
				}
			}
			//说明链表中也没有了可以申请的空间,此时就要依赖我们的一级空间配置器
			heap_size += bytes_to_get;
			_start_free += bytes_to_get;
			return (ChunkAllocate(bytes, nobjs));
		}
	}
private:
	char* _start_free;
	char* _end_free;

	enum{_ALIGIN = 8};//这是自由链表每一格代表的空间数
	enum{_MAX_BYTES = 128};//自由链表总共空间的大小
	enum{_FREE_LIST_SIZE=_MAX_BYTES/_ALIGIN};

	typedef union Obj{
		union Obj* free_list_link;
		char client_data[1];
	}Obj;//将自由链表想象成哈希表,下面还会挂链表

	Obj* _free_list[_FREE_LIST_SIZE];//顺序表,每个元素是一个Obj*的对象

};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值