空間配置器:爲了解決因頻繁小塊開闢時,產生的內存碎片問題。
如果開闢的內存大於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};