內(nèi)存池是自己向OS請(qǐng)求的一大塊內(nèi)存,自己進(jìn)行管理。
##系統(tǒng)調(diào)用## 我們先測(cè)試系統(tǒng)調(diào)用new/delete的用時(shí)。
#include <IOStream>
#include <time.h> using namespace std; class TestClass { private: char m_chBuf[4096]; }; timespec diff(timespec start, timespec end) { timespec temp; temp.tv_sec = end.tv_sec-start.tv_sec; temp.tv_nsec = end.tv_nsec-start.tv_nsec; return temp; } int main() { timespec time1, time2; clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time1); for(unsigned int i=0; i< 0x5fffff; i++) { TestClass *p = new TestClass; delete p; } clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time2); cout<<diff(time1,time2).tv_sec<<":"<<diff(time1,time2).tv_nsec<<endl; }
用時(shí)為604124400ns。系統(tǒng)的new是在堆上分配資源,每次執(zhí)行都會(huì)分配然后銷毀。
##簡(jiǎn)單的內(nèi)存池##
#include <iostream> #include <time.h> using namespace std; char buf[4100]; //已分配內(nèi)存 class TestClass { public: void* operator new(size_t) {return (void*)buf;} void operator delete(void* p){} private: char m_chBuf[4096]; }; int main() { timespec time1, time2; clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time1); for(unsigned int i=0; i< 0x5fffff; i++) { TestClass *p = new TestClass; delete p; } clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time2); cout<< diff(time1,time2).tv_sec<<":"<<diff(time1,time2).tv_nsec<< endl; }
用時(shí)為39420791ns,后者比前者快20倍。
簡(jiǎn)單內(nèi)存池在開始在全局/靜態(tài)存儲(chǔ)區(qū)分配資源,一直存在。每次重載的new調(diào)用只是返回了buf的地址,所以快。
##MemPool定義##
class CMemPool { private: struct _Unit { struct _Unit *pPrev, *pNext; }; void* m_pMemBlock; struct _Unit* m_pFreeMemBlock; struct _Unit* m_pAllocatedMemBlock; unsigned long m_ulUnitSize; //一個(gè)單元的內(nèi)存大小 unsigned long m_ulBlockSize; //整個(gè)內(nèi)存池的內(nèi)存大小 public: CMemPool(unsigned long lUnitNum = 50, unsigned long lUnitSize = 1024); ~CMemPool(); void* Alloc(unsigned long ulSize, bool bUseMemPool = true); void Free(void* p); };
CMemPool定義了一個(gè)_Unit來管理鏈表,指針被包含在整個(gè)結(jié)構(gòu)中,這種方式和內(nèi)核中的鏈表寫法很像。
m_pMemBlock指向分配的那塊大小為m_ulBlockSize的大內(nèi)存的地址。m_pMemBlock是線性的內(nèi)存,我們把它用下列這種方式管理。

它被均分為lUnitNum個(gè)大小為m_ulUnitSize Byte的小內(nèi)存塊。每個(gè)塊分為2部分:Unit鏈表管理頭,真正進(jìn)行存儲(chǔ)的內(nèi)存單元。
從圖中可以看出m_ulBlockSize的計(jì)算方式為:
UnitNum * ( UnitSize + sizeof(Struct _Unit))
然后用雙向鏈表連接所有的小塊。m_pFreeMemBlock指向空閑的內(nèi)存的起始位置,m_pAllocatedMemBlock指向已分配出去的內(nèi)存的起始位置。
##MemPool實(shí)現(xiàn)##
CMemPool::CMemPool(unsigned long ulUnitNum, unsigned long ulUnitSize): m_pMemBlock(NULL), m_pAllocatedMemBlock(NULL), m_pFreeMemBlock(NULL), m_ulBlockSize(ulUnitNum * (ulUnitSize+sizeof(struct _Unit))), m_ulUnitSize(ulUnitSize) { m_pMemBlock = malloc(m_ulBlockSize); if(NULL != m_pMemBlock) { for(unsigned long i = 0; i<ulUnitNum; i++) { struct _Unit* pCurUnit=(struct _Unit*)((char*)m_pMemBlock + i*(ulUnitSize+sizeof(struct _Unit)) ); pCurUnit->pPrev = NULL; pCurUnit->pNext = m_pFreeMemBlock; if(NULL != m_pFreeMemBlock) { m_pFreeMemBlock->pPrev = pCurUnit; } m_pFreeMemBlock = pCurUnit; } } }
構(gòu)造函數(shù)設(shè)置默認(rèn)的小塊數(shù)為50,每個(gè)小快大小為1024,最后用雙向鏈表管理它們,m_pFreeMemBlock指向開始。
void* CMemPool::Alloc(unsigned long ulSize, bool bUseMemPool) { if(ulSize > m_ulUnitSize || false == bUseMemPool || NULL == m_pMemBlock || NULL == m_pFreeMemBlock) { cout << "System Call" << endl; return malloc(ulSize); } struct _Unit *pCurUnit = m_pFreeMemBlock; m_pFreeMemBlock = pCurUnit->pNext; if(NULL != m_pFreeMemBlock) { m_pFreeMemBlock->pPrev = NULL; } pCurUnit->pNext = m_pAllocatedMemBlock; if(NULL != m_pAllocatedMemBlock) { m_pAllocatedMemBlock->pPrev = pCurUnit; } m_pAllocatedMemBlock = pCurUnit; cout << "Memory Pool" << endl; return (void*)((char*)pCurUnit + sizeof(struct _Unit)); }
Alloc的作用是分配內(nèi)存,返回分配的內(nèi)存地址,注意加上Unit的大小是為了略過Unit管理頭。實(shí)質(zhì)是把m_pFreeMemBlock指向的free內(nèi)存移動(dòng)到m_pAllocatedMemBlock指向的已分配內(nèi)存里。
每次分配時(shí),m_pFreeMemBlock指針后移。pCurUnit從前面插入到m_pAllocatedMemBlock里。
void CMemPool::Free(void* p) { if(m_pMemBlock<p && p<(void*)((char*)m_pMemBlock + m_ulBlockSize)) { //判斷釋放的內(nèi)存是不是處于CMemPool cout << "Memory Pool Free" << endl; struct _Unit* pCurUnit = (struct _Unit*)((char*)p - sizeof(struct _Unit)); m_pAllocatedMemBlock = pCurUnit->pNext; if(NULL != m_pAllocatedMemBlock) { m_pAllocatedMemBlock->pPrev == NULL; } pCurUnit->pNext = m_pFreeMemBlock; if(NULL != m_pFreeMemBlock) { m_pFreeMemBlock->pPrev = pCurUnit; } m_pFreeMemBlock = pCurUnit; } else { free(p); } }
Free的作用是釋放內(nèi)存,實(shí)質(zhì)是把m_pAllocatedMemBlock指向的已分配內(nèi)存移動(dòng)到m_pFreeMemBlock指向的free內(nèi)存里。和Alloc的作用相反。
pCurUnit要減去struct _Unit是為了從存儲(chǔ)單元得到管理頭的位置,堆是向上生長(zhǎng)的。
##測(cè)試##
#include "mempool.h" #include <time.h> CMemPool g_MemPool; class CTestClass { public: void *operator new(size_t); //重載運(yùn)算符new void operator delete(void *p); private: char m_chBuf[1000]; }; void *CTestClass::operator new(size_t uiSize) { return g_MemPool.Alloc(uiSize); //分配g_MemPool的內(nèi)存給它 } void CTestClass::operator delete(void *p) { g_MemPool.Free(p); } class CTestClass2 { private: char m_chBuf[1000]; }; timespec diff(timespec start, timespec end) { timespec temp; temp.tv_sec = end.tv_sec-start.tv_sec; temp.tv_nsec = end.tv_nsec-start.tv_nsec; return temp; } int main() { timespec time1, time2; for(int iTestCnt=1; iTestCnt<=10; iTestCnt++) { unsigned int i; //使用內(nèi)存池測(cè)試 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time1); for(i=0; i<100000*iTestCnt; i++) { CTestClass *p = new CTestClass; delete p; } clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time2); cout << "[ Repeat " << 100000*iTestCnt << " Times ]" << "Memory Pool Interval = " << diff(time1,time2).tv_nsec << "ns" << endl; //使用系統(tǒng)調(diào)用測(cè)試 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time1); for(i=0; i<LOOP_TIMES; i++) { CTestClass2 *p = new CTestClass2; delete p; } clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &time2); cout << "[ Repeat " << LOOP_TIMES << " Times ]" << "System Call Interval = " << diff(time1,time2).tv_nsec << "ns" << endl; } return 0; }
##結(jié)果##
從下圖可以看出,只有當(dāng)程序頻繁地用系統(tǒng)調(diào)用malloc/free或者new/delete分配內(nèi)存時(shí),內(nèi)存池有價(jià)值。
