空間配置器

空間配置器:爲了解決因頻繁小塊開闢時,產生的內存碎片問題。

如果開闢的內存大於128個字節時,就調用一級空間配置器,小於128個字節時,就調用二級空間配置器。

一級空間配器:封裝了malloc()和free()。


注意:如果客戶端設置的內存不足處理函數,沒有設置好,會存在死循環的危險。

//一級空間配置器
template <int inst>
class __Malloc_Alloc_Template 
{
private:
	static void *Oom_Malloc(size_t); //內存不足處理函數
	static MALLOCALLOC _Malloc_Alloc_Oom_Handler; //函數指針,內存不足的時候的處理機制
public:
	static void* Allocate(size_t n) //n>128,就malloc直接開闢
	{
		void *result = malloc(n);
		if (0 == result)   //開闢失敗
			result = Oom_Malloc(n);//調用內存不足處理函數
		return result;
	}
		
	static void Deallocate(void *p)  //釋放內存
	{
		free(p);
	}

	static void (* Set_Malloc_Handler(MALLOCALLOC f) //讓用戶來設置內存不足時的處理機制
	{
		MALLOCALLOC old = _Malloc_Alloc_Oom_Handler;
		_Malloc_Alloc_Oom_Handler = f;
		return old;
	}
};

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)
{
	MALLOCALLOC My_Malloc_Handler; //定義函數指針
	void* result;
	while(1)
	{
		My_Malloc_Handlerr=_Malloc_Alloc_Oom_Handler;
		if(My_Malloc_Handlerr==0) //不設置處理機制,直接拋出異常
		{
			throw bad_alloc();
		}
		(*My_Malloc_Handlerr)();  //調用內存不足處理機制的函數,申請釋放其他地方的內存 
		result=malloc(n);
		if(result)
		{
			return result;
			break;
		}
	}
}
二級空間配置器:用到了自由鏈表和內存池。假設要分配8個字節大小的空間,那麼他就會去內存池中分配多個8個字節大小的內存塊(20*n),將多餘的掛在自由鏈表上,下一

次再需要8個字節時就去自由鏈表上取就可以了,如果回收這8個字節的話,直接將它掛

在自由鏈表上就可以了。




//二級空間配置器
template <bool threads, int inst>
class __Default_Alloc_Template 
{
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 size_t ROUND_UP(size_t bytes) //把bytes取向上取成8的整數倍
	{
        return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));
    }
	
	static  size_t FREELIST_INDEX(size_t bytes) 
	    //用來計算bytes大小的空閒區在鏈表的那個位置上掛着
	{
        return (((bytes) + __ALIGN-1)/__ALIGN - 1);
	}
	static obj * volatile free_list[__NFREELISTS]; //定義一個自由鏈表
	static char *start_free; //內存池的頭指針
    static char *end_free;   //內存池的尾指針
    static size_t heap_size; //記錄內存池已經向系統申請了多大的內存
  
public:
  static void * Allocate(size_t n) //n<128,分配空間
  {
		obj * volatile * my_free_list;  //防止編譯器優化,防止使另一個線程可以修改
		void* result;

		if (n > (size_t) __MAX_BYTES)     //n>128,直接調用一級空間配置器
		{
			return Malloc_Alloc::Allocate(n);
		}

		//在自由鏈表中找
		my_free_list = free_list + FREELIST_INDEX(n); //指向在自由鏈表的那個位置上
		result = *my_free_list; //result指向這個節點下面掛的空間
		if (result == 0) //這個節點下沒有內存
		{
			void *r = refill(ROUND_UP(n)); //去內存池申請
			return r;
		}
		//掛着空閒內存,直接返回
		*my_free_list = result -> free_list_link; //把第二塊空間地址掛到自由鏈表節點下面
		return result;
  }

  static void deallocate(void *p, size_t n)
	  {
		obj *q = (obj *)p;
		obj * volatile * my_free_list;

		if (n > (size_t) __MAX_BYTES) //n>128
		{
			__Malloc_Alloc_Template<0>::Deallocate(q);
			return;
		}
		//頭插到自由鏈表中
		my_free_list = free_list + FREELIST_INDEX(n);
		q -> free_list_link = *my_free_list;
		*my_free_list = q;
  }
private:
  static void* refill(size_t n)//去內存池申請
  {
	    int nobjs = 20; //向內存池一次性申請20個
		char * chunk = chunk_alloc(n, nobjs); 
		obj * volatile * my_free_list;
		if (1 == nobjs) 
			return chunk;
		obj* result = (obj *)chunk;    //將申請的第一個對象返回
		 
		my_free_list = free_list + FREELIST_INDEX(n);
		//將剩餘的掛到自由鏈表上
		*my_free_list = (obj *)(chunk + n); 
		obj* cur=*my_free_list;
		for (int i = 2;i<nobjs; i++) 
		{
			obj* next= (obj*)(chunk + n*i);  
            cur->_freeListLink = next;  
            cur = next; 
		} 
		cur->_freeListLink = 0;  
		
	    return result;
  }
  char* chunk_alloc(size_t size, 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;
        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 New = 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;
        }

        start_free = (char *)malloc(New);
        if (0 == start_free) //沒有內存,在自由鏈表中找一個比n的內存
		{
            obj * volatile * my_free_list, *p;
            for (int i = size; i <= __MAX_BYTES; i += __ALIGN) 
			{
                my_free_list = free_list + FREELIST_INDEX(i);
                p = *my_free_list;
                if (0 != p) //找到一塊內存
				{
                    *my_free_list = p -> free_list_link;
                    start_free = (char *)p;  //將這塊空間還給內存池
                    end_free = start_free + i;
                    return chunk_alloc(size, nobjs);
                }
            }
			//要是再找不到的話,就調一級空間配置器,其中有內存不足處理機制,
			//要是還不行的話,他會自動拋出異常
	        end_free = 0;	
            start_free = (char *)__Malloc_Alloc_Template<0>::Allocate(New); 
        }
        heap_size += New;
        end_free = start_free + New;
        return chunk_alloc(size, nobjs); //在調用一次來分配內存
    }
 

};

template <bool threads, int inst>
char *__Default_Alloc_Template<threads,inst>::start_free=0; 
template <bool threads, int inst>
char *__Default_Alloc_Template<threads,inst>::end_free=0;   
template <bool threads, int inst>
size_t __Default_Alloc_Template<threads,inst>::heap_size=0; 
template <bool threads, int inst>
class _DefaultAllocTemplate<threads, inst>::obj * volatile
_DefaultAllocTemplate<threads, inst>::free_list[__NFREELISTS]={0};



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章