STL空间配置器

Posted ZDF0414

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STL空间配置器相关的知识,希望对你有一定的参考价值。

STL空间配置器分为第一级配置器与第二级配置器,主要用来对内存的申请与释放。

第一级配置器:调用的是malloc/free,并有 new-handle 机制

第二级配置器:由自由链表及内存池组成

                           自由链表一个存储16个空闲块列表表头的数组free_list;

                      内存池起始地址的指针start_free和一个指向结束地址的指针end_free之间的内存

程序中的小对象的分配极易造成内存碎片(也叫外碎片),给操作系统的内存管理带来了很大压力,系统中碎片的增多不但会影响内存分配的速度,而且会极大地降低内存的利用率。

使用空间配置器,提高了空间的利用率和内存的分配速度,主要原因是:

(1)当内存申请超过128字节时,就会被视为大块内存,由第一级空间配置器处理;

(2)当内存申请小于等于128字节时,会对其使用二级空间配置

       a) 先查找自由链表,如有空间,直接分配即可;

       b)当a)不满足时,便会向内存池申请大块内存,一块返回申请,另外的挂到自由链表上,当反复申请该大小内存时,便极大的 提高了分配速度;

       c)当内存池内存不足时,只返回满足要求的申请即可,适量分配空间;

       d) 当内存池也满足不了要求时,便会向系统 malloc 一大段空间给内存池,再继续满足申请

       e) 当系统内存紧张时,便会返回到看自由链表是否有闲置空间可释放至内存池;

       f)  到山穷水尽的地步,便移交第一级空间配置器处理

优点:(1)小对象的内存申请与释放快速(不用每次都要操作系统完成);

        (2)解决了内存碎片的问题(外碎片);

        (3)提高了空间利用率

缺点:引入了内碎片的问题

//第一级空间配置
template<int inst>
class __MallocAllocTemplate

public:
	static void *Oom_Malloc(size_t size)
	
		void(*my_malloc_handler)();
		void *result;

		while (1)
		
			my_malloc_handler = __Malloc_Alloc_Oom_Handler;
			if (my_malloc_handler == NULL)
			
				throw bad_alloc();
			
			(*my_malloc_handler)();
			result = malloc(size);
			if (result) 
				return(result);
		
	
	static void * Allocate(size_t n)
	
		void *result = malloc(n);
		if (0 == result)
			result = Oom_Malloc(n);
		return result;
	

	static void Deallocate(void *p, size_t /* n */)
	
		free(p);
	
	static void(*Set_Malloc_Handler(void(*f)()))()
	
		void(*old)() = __Malloc_Alloc_Oom_Handler;
		__Malloc_Alloc_Oom_Handler = f;
		return(old);
	
private:
	static void(*__Malloc_Alloc_Oom_Handler)();
;
template<int inst>
void(*__MallocAllocTemplate<inst>::__Malloc_Alloc_Oom_Handler)() = NULL;


//第二级空间配置
template <bool threads, int inst>
class __Default_Alloc_Template

public:
	static void* Allocate(size_t n)
	
		if (n > __MAX_BYTES)
			return __MallocAllocTemplate<0>::Allocate(n);
		size_t index = FREELIST_INDEX(n);
		void* result = NULL;
		if (free_list[index] == NULL)//如果自由串上没有内存,就去内存池上找空间
			result = Refill(ROUND_UP(n));
		else
		
			result = free_list[index];
			free_list[index] = free_list[index]->free_list_link;
		
		return result;
	
	static void* Refill(size_t n)
	
		size_t nobjs = 20;
		char * chunk = chunk_alloc(n, nobjs);//通过chunk_alloc函数获得内存
		void* result = (void*)chunk;//把第一个满足的空间分配返回给申请者
		if (nobjs == 1)
			return result;

		//把剩下的多分出的内存空间挂到自由链表上
		else
		
			size_t index = FREELIST_INDEX(n);
			obj* cur = NULL;
			obj* next = (obj*)((char*)chunk + n);
			free_list[index] = next;
			size_t i = 1;
			for (; i < nobjs; i++)
			
				cur = next;
				next = (obj*)((char*)next + n);
				cur->free_list_link = next;
			
			cur->free_list_link = NULL;
		
		return result;
	
	static char* chunk_alloc(size_t n, size_t& nobjs)
	
		size_t totalbytes = n*nobjs;
		size_t poolbytes = _end_free - _start_free;//内存池现有的内存情况
		char* result;//返回分配的内存
		if (poolbytes >= totalbytes)
		
			result = _start_free;
			_start_free += totalbytes;//降低水位线,内存池内存减少
			return  result;
		
		else if (poolbytes > n)//内存池满足不了一次分配20个该大小的内存,但至少满足一个(够需求)
		
			nobjs = poolbytes / n;//当前内存池可满足多少个n
			totalbytes = n*nobjs;
			result = _start_free;
			_start_free += totalbytes;
			return result;
		
		else
		//内存池的内存不足

			//重新为内存池申请大小,大小为bytes_to_get
			size_t bytes_to_get = 2 * totalbytes + ROUND_UP(_heap_size >> 4);
			//1、先把内存池中剩余的内存挂到自由链表的相应位置上
			if (poolbytes > 0)
			
				size_t index = FREELIST_INDEX(poolbytes);
				((obj*)_start_free)->free_list_link = free_list[index];
				free_list[index] = (obj*)_start_free;
			
			_start_free = (char*)malloc(bytes_to_get);
			if (_start_free == 0)
			//申请内存失败
				//解决方法:在自由链表上比它大的位置查找是否有空闲内存
				for (size_t i = n; i <= __MAX_BYTES; i += __ALIGN)
				
					size_t index = FREELIST_INDEX(n);
					obj* p = free_list[index];
					if (p != 0)
					//找到满足要求的内存
						free_list[index] = free_list[index]->free_list_link;
						_start_free = (char*)p;
						_end_free = _start_free + n;
						return chunk_alloc(n, nobjs);
					
				
				//自由链表上没有可提供的内存,就只能转第一种空间配置的方法了
				_start_free = (char*)__MallocAllocTemplate<0>::Allocate(bytes_to_get);
			
			_heap_size += bytes_to_get;
			_end_free = _start_free + bytes_to_get;
			return(chunk_alloc(n, nobjs));
		
	
	static void Deallocate(void*p, size_t n)
	
		if (n > __MAX_BYTES)
		
			__MallocAllocTemplate<0>::Deallocate(p, n);
		
		else
		
			obj* tmp = (obj*)p;
			size_t index = FREELIST_INDEX(n);
			tmp->free_list_link = free_list[index];
			free_list[index] = tmp;
		
	
protected:
	static size_t FREELIST_INDEX(size_t n)
	
		return (n + __ALIGN - 1) / __ALIGN - 1;
	
	static size_t ROUND_UP(size_t bytes)//保证字节数与8字节对齐
	
		return (((bytes)+__ALIGN - 1) & ~(__ALIGN - 1));
	
private:
	enum  __ALIGN = 8 ;
	enum  __MAX_BYTES = 128 ;
	enum  __NFREELISTS = __MAX_BYTES / __ALIGN ;

	union obj 
	
		union obj * free_list_link;//下一个节点
		char client_data[1];    /* The client sees this.        */
	;


	//自由链表
	static obj *  free_list[__NFREELISTS];

	//内存池
	static char * _start_free;
	static char * _end_free;
	static size_t _heap_size;//总共为内存池分配的空间字节数
;

template <bool threads, int inst>
typename __Default_Alloc_Template<threads, inst>::obj *   __Default_Alloc_Template<threads, inst>::free_list[__NFREELISTS] =  NULL ;


template <bool threads, int inst>
char* __Default_Alloc_Template<threads, inst>::_start_free = NULL;

template <bool threads, int inst>
char* __Default_Alloc_Template<threads, inst>::_end_free = NULL;

template <bool threads, int inst>
size_t __Default_Alloc_Template<threads, inst>::_heap_size = 0;



以上是关于STL空间配置器的主要内容,如果未能解决你的问题,请参考以下文章

STL——空间配置器

STL空间配置器

STL空间配置器

STL空间配置器

STL实现简单的空间配置器

STL实现简单的空间配置器