// 徹底解析STL空間配置器
/***** 一級配置區 ****************************/
// 1. 採用mallo/relloc/free申請和釋放內存
// 2. 處理內存申請失敗的處理
// (1)設置set_new_handle,若爲NULL拋出__THROW_BAD_ALLOC;
// (2)嘗試重複申請
/**********************************************/
#pragma once
class CFirstLevelAlloc;
class CSecondLevelAlloc;
#ifdef _CHUNK_ALLOC
typedef CFirstLevelAlloc SelfAlloc;
#else
typedef CSecondLevelAlloc SelfAlloc;
#endif
typedef void(*CallBackFunc)();
class CFirstLevelAlloc
{
public:
CFirstLevelAlloc();
static CallBackFunc m_CallBackFunc;
static void* Allocate(size_t nSize);
static void* Allocate(void *p, size_t nSize);
static void Deallocate(void *p, size_t nSize = 0);
static void SetCallBackHandle(CallBackFunc cb);
private:
static void* ReAllocate(size_t nSize);
static void* ReAllocate(void *p, size_t nSize);
};
enum {ALIGN = 8}; // 小型區塊的上調邊界
enum {MAX_BYTES = 128}; // 小型區塊的上限
enum {FREELISTNUM = MAX_BYTES/ALIGN}; // free-lists個數
// 空閒內存鏈表結構
union FreeList
{
union FreeList *pFreeList;
char client_data[1];
};
#include "stdio.h"
#include "alloc_define.h"
#include <stdlib.h>
#include <iostream>
using namespace std;
CallBackFunc CFirstLevelAlloc::m_CallBackFunc = NULL;
CFirstLevelAlloc::CFirstLevelAlloc()
{
}
void* CFirstLevelAlloc::Allocate(size_t nSize)
{
void *result = malloc(nSize);
if (NULL == result)
{
result = ReAllocate(nSize);
}
return result;
}
void* CFirstLevelAlloc::Allocate(void *p, size_t nSize)
{
void *result = realloc(p, nSize);
if (NULL == result)
{
result = ReAllocate(p, nSize);
}
return result;
}
void* CFirstLevelAlloc::ReAllocate(size_t nSize)
{
while (1)
{
if (NULL == m_CallBackFunc)
{
cout << "bad alloc!" << endl;
return NULL;
}
else
{
m_CallBackFunc();
void *result = Allocate(nSize);
if (result)
{
return result;
}
}
}
}
void* CFirstLevelAlloc::ReAllocate(void *p, size_t nSize)
{
while (1)
{
if (NULL == m_CallBackFunc)
{
cout << "bad alloc!" << endl;
return NULL;
}
else
{
m_CallBackFunc();
void *result = Allocate(p, nSize);
if (result)
{
return result;
}
}
}
}
void CFirstLevelAlloc::Deallocate(void *p, size_t nSize)
{
free(p);
}
void CFirstLevelAlloc::SetCallBackHandle(CallBackFunc cb)
{
m_CallBackFunc = cb;
}
class CSecondLevelAlloc
{
public:
CSecondLevelAlloc();
static void* Allocate(size_t nSize);
static void Deallocate(void *p, size_t nSize);
static void SetCallBackHandle(CallBackFunc cb);
private:
static size_t FreeListIndex(int nBytes); // 根據區塊大小獲得freelist索引
static size_t Round_Up(int nBytes); // 將bytes按內存對齊上調至ALIGN的倍數
static char *ChunkAlloc(size_t nSize, int& nObjs);
static void* Refill(size_t nSize);
private:
static FreeList *m_pFreeList[FREELISTNUM];
static char *m_pStartFree;
static char *m_pEndFree;
static size_t m_nHeapSize;
};
FreeList* CSecondLevelAlloc::m_pFreeList[FREELISTNUM] = { 0 };
char* CSecondLevelAlloc::m_pStartFree = NULL;
char* CSecondLevelAlloc::m_pEndFree = NULL;
size_t CSecondLevelAlloc::m_nHeapSize = 0;
CSecondLevelAlloc::CSecondLevelAlloc()
{
}
void* CSecondLevelAlloc::Allocate(size_t nSize)
{
// 首先判斷nSize,若大於128則調用一級配置器,不然調用二級配置器
if (nSize > (size_t)MAX_BYTES)
{
cout << "調用1級配置器配置內存空間,空間大小:" << nSize << endl;
return (CFirstLevelAlloc::Allocate(nSize));
}
cout << "調用2級配置器配置內存空間,空間大小:" << nSize << endl;
FreeList **pFreeList = m_pFreeList + FreeListIndex(nSize);
if (*pFreeList == NULL)
{
return Refill(Round_Up(nSize));
}
FreeList *p = *pFreeList;
*pFreeList = p->pFreeList;
return p;
}
void CSecondLevelAlloc::Deallocate(void *p, size_t nSize)
{
// 首先判斷nSize,若大於128則調用一級配置器,不然調用二級配置器
if (nSize > MAX_BYTES)
{
cout << "調用1級配置器釋放內存空間,空間大小:" << nSize << endl;
return CFirstLevelAlloc::Deallocate(p);
}
cout << "調用2級配置器釋放內存空間,空間大小:" << nSize << endl;
FreeList **pFreeList = m_pFreeList + FreeListIndex(Round_Up(nSize));
((FreeList*)p)->pFreeList = *pFreeList;
*pFreeList = (FreeList*)p;
}
size_t CSecondLevelAlloc::FreeListIndex(int nBytes)
{
return ((nBytes + ALIGN) / ALIGN - 1);
}
size_t CSecondLevelAlloc::Round_Up(int nBytes)
{
return ((nBytes + ALIGN - 1) & (~(ALIGN - 1)));
}
char* CSecondLevelAlloc::ChunkAlloc(size_t nSize, int& nObjs)
{
char *pResult = NULL;
size_t nTotalBytes = nSize * nObjs;
size_t nBytesLeft = m_pEndFree - m_pStartFree;
if (nBytesLeft >= nTotalBytes)
{
// 內存池剩餘空間徹底知足需求量
pResult = m_pStartFree;
m_pStartFree += nTotalBytes;
return pResult;
}
else if (nBytesLeft >= nSize)
{
// 內存池剩餘空間不能徹底知足需求量,但足夠供應一個或一個以上的區塊
nObjs = nBytesLeft / nSize;
pResult = m_pStartFree;
m_pStartFree += (nObjs * nSize);
return pResult;
}
else
{
// 內存池剩餘空間連一個區塊的大小都沒法提供,就調用malloc申請內存,新申請的空間是需求量的兩倍
// 與隨着配置次數增長的附加量,在申請以前,將內存池的殘餘內存回收
size_t nBytesToGet = 2 * nTotalBytes + Round_Up(m_nHeapSize >> 4);
// 如下試着讓內存池中的殘餘零頭還有價值
if (nBytesLeft > 0)
{
// 內存池內還有一些零頭,先配給適當的freelist
// 首先尋找適當的freelist
FreeList *pFreeList = m_pFreeList[FreeListIndex(nBytesLeft)];
// 調整freelist,將內存池中的殘餘空間編入
((FreeList*)m_pStartFree)->pFreeList = pFreeList;
pFreeList = (FreeList*)m_pStartFree;
}
// 配置heap空間
m_pStartFree = (char *)malloc(nBytesToGet);
if (NULL == m_pStartFree)
{
//若是沒有申請成功,若是free_list當中有比n大的內存塊,這個時候將free_list中的內存塊釋放出來.
//而後將這些內存編入本身的free_list的下標當中.調整nobjs.
int i;
FreeList **pFreeList, *p;
for (i = nSize; i < MAX_BYTES; i += ALIGN)
{
pFreeList = m_pFreeList+FreeListIndex(i);
p = *pFreeList;
if (NULL != p)
{
// freelist內尚有未用區塊
// 調整freelist以釋放未用區塊
*pFreeList = p->pFreeList;
m_pStartFree = (char *)p;
m_pEndFree = m_pStartFree + i;
// 調整本身,爲了修正nobjs
return (ChunkAlloc(nSize, nObjs));
}
}
m_pEndFree = NULL; // 若是出現意外(山窮水盡,處處都沒有內存可用)
// 調用1級配置器,看out-of-range機制能不能出點力
m_pStartFree = (char*)CFirstLevelAlloc::Allocate(nBytesToGet);
}
m_nHeapSize += nBytesToGet;
m_pEndFree = m_pStartFree + nBytesToGet;
return (ChunkAlloc(nSize, nObjs));
}
}
// 當freelist中沒有可用的區塊了時,就調用ReFill從新填充空間
// 新的空間將取自內存池,缺省爲20個新節點
// 但萬一內存池空間不足,得到的節點數可能小於20
void* CSecondLevelAlloc::Refill(size_t nSize)
{
int nObjs = 20; // 默認每一個鏈表組右20個區塊
char *pChunk = ChunkAlloc(nSize, nObjs);
if (1 == nObjs)
{
// 若是得到一個區塊,這個區塊就分配給調用者,freelist無新節點
return pChunk;
}
// 如有多餘的區塊,則將其添加到對應索引的freelist中
FreeList **pFreeList = m_pFreeList + FreeListIndex(nSize);
FreeList *pResult = (FreeList *)pChunk; // 這一塊準備返回給客戶端
FreeList *pCurrent = NULL;
FreeList *pNext = NULL;
*pFreeList = pNext = (FreeList*)(pChunk + nSize);
for (int i = 1; i < nObjs; i++)
{
pCurrent = pNext;
pNext = (FreeList*)((int)pNext + nSize);
pCurrent->pFreeList = pNext;
}
pCurrent->pFreeList = NULL;
return pResult;
}
void CSecondLevelAlloc::SetCallBackHandle(CallBackFunc cb)
{
CFirstLevelAlloc::m_CallBackFunc = cb;
}
#pragma once
#include "alloc_define.h"
template<typename T, typename Alloc = SelfAlloc>
class CSimpleAlloc
{
public:
static T* Allocate(size_t n)
{
if (n == 0)
{
return NULL;
}
return (T *)Alloc::Allocate(n * sizeof(T));
}
static T* Allocate(void)
{
return (T *)Alloc::Allocate(sizeof(T));
}
static void Deallocate(T *p, size_t n)
{
if (n != 0)
{
Alloc::Deallocate(p, n * sizeof(T));
}
}
static void Deallocate(T *p)
{
Alloc::Deallocate(p, sizeof(T));
}
static void SetCallBackHandle(CallBackFunc cb)
{
Alloc::SetCallBackHandle(cb);
}
};
#include "stdio.h"
#include<iostream>
using namespace std;
#include"stl_test.h"
#include "simple_alloc.h"
#include<vector>
void Func()
{
cout << "調用回調函數處理內存不足的狀況" << endl;
// 爲了防止死循環,該函數應該加上一個判斷條件若是它本次沒有清理出空間,那麼就拋出異常
}
template <class T, class Alloc = SelfAlloc>
class A
{
public:
A() :m_ptr(NULL), m_nSize(0){}
A(size_t nSize)
{
DataAllocator::SetCallBackHandle(Func);
m_nSize = nSize;
m_ptr = DataAllocator::Allocate(nSize);
for (int i = 0; i < (int)nSize; i++)
{
m_ptr[i] = i;
cout << m_ptr[i] << " ";
}
cout << endl;
}
~A()
{
DataAllocator::Deallocate(m_ptr, m_nSize);
}
private:
T *m_ptr;
size_t m_nSize;
typedef CSimpleAlloc<T, Alloc> DataAllocator;
};
void main()
{
A<int> a(11);
A<int> b(50);
a.~A();
b.~A();
}