在需要頻繁地 new/delete 對(duì)象時(shí),很容易造成對(duì)象分配慢、內(nèi)存碎片的產(chǎn)生。為提升應(yīng)用程序在分配對(duì)象內(nèi)存的效率,可以使用內(nèi)存池管理對(duì)象內(nèi)存的分配和釋放。
作者按以下方式實(shí)現(xiàn):
- 編寫模板類 CMemoryPool, 為每個(gè)類創(chuàng)建一個(gè)靜態(tài)的內(nèi)存池對(duì)象
- 內(nèi)存池類按大塊向系統(tǒng)申請(qǐng)內(nèi)存,將內(nèi)存以靜態(tài)雙向鏈表進(jìn)行關(guān)聯(lián),形成空閑鏈表
- 分配類對(duì)象內(nèi)存時(shí),從空閑鏈表淘汰一個(gè)元素,并將此結(jié)點(diǎn)添加到已分配的雙向鏈表中
- 在內(nèi)存池中將分配過的內(nèi)存結(jié)點(diǎn)使用雙向鏈表關(guān)聯(lián),同時(shí)實(shí)現(xiàn)應(yīng)用程序退出后內(nèi)存泄漏自動(dòng)檢查
- 釋放類對(duì)象內(nèi)存時(shí),從已分配的雙向鏈表中移除結(jié)點(diǎn)元素,并歸還給空閑鏈表中
- 提供宏為類添加內(nèi)存池的靜態(tài)成員對(duì)象,并重載類的 operator new 和 operator delete 運(yùn)算符
- 不允許構(gòu)造對(duì)象數(shù)組(此內(nèi)存池內(nèi)不支持動(dòng)態(tài)創(chuàng)建對(duì)象數(shù)組)
- 在使用時(shí),只需要在類定義中加入 DECLARE_MEMORY_POOL 宏聲明,在實(shí)現(xiàn)文件中加入 IMPLEMENT_MEMORY_POOL 宏實(shí)現(xiàn)
MemoryPool.h 文件中實(shí)現(xiàn)了模板類 CMemoryPool, 代碼如下
#ifndef ___MEMORY__POOL__20141227___
#define ___MEMORY__POOL__20141227___
#include "OSType.h"
#include "Mutex.h"
#include <list>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#define DECLARE_MEMORY_POOL(className) \
private: \
static CMemoryPool<className> m_memoryPool ## className; \
public: \
void* operator new(size_t size) \
{ \
return m_memoryPool ## className.Allocate(); \
} \
void operator delete(void *pBuffer) \
{ \
m_memoryPool ## className.Free(pBuffer); \
} \
void* operator new[](size_t size); \
void operator delete[](void *pBuffer); \
private:
#define IMPLEMENT_MEMORY_POOL(className) \
CMemoryPool<className> className::m_memoryPool ## className(#className);
template <typename T>
struct SMemoryPoolNode
{
T obj;
SMemoryPoolNode* pPrev;
SMemoryPoolNode* pNext;
};
// 定義內(nèi)存池塊的大小
#define MEMORY_POOL_BLOCK_SIZE 1048576LL
template <typename T>
class CMemoryPool
{
public:
CMemoryPool(LPCSTR lpszClassName);
~CMemoryPool();
public:
void* Allocate();
void Free(void *pBuffer);
private:
Bool NeedMoreBlock();
private:
std::string m_strClassName;
size_t m_nBlockSize;
std::list<void *> m_lstBlockList;
SMemoryPoolNode<T>* m_pFreeList;
SMemoryPoolNode<T>* m_pUsedList;
CMutex m_lock;
};
template <typename T>
CMemoryPool<T>::CMemoryPool(LPCSTR lpszClassName) : m_strClassName(lpszClassName)
{
m_nBlockSize = MEMORY_POOL_BLOCK_SIZE;
m_nBlockSize = m_nBlockSize / sizeof(SMemoryPoolNode<T>) * sizeof(SMemoryPoolNode<T>);
if (sizeof(SMemoryPoolNode<T>) > m_nBlockSize)
{
m_nBlockSize = 10 * sizeof(SMemoryPoolNode<T>);
}
m_pFreeList = NULL;
m_pUsedList = NULL;
NeedMoreBlock();
}
template <typename T>
CMemoryPool<T>::~CMemoryPool()
{
// 打印出未釋放的內(nèi)存
if (m_pUsedList != NULL)
{
SMemoryPoolNode<T> *ptr = m_pUsedList;
while (ptr)
{
fprintf(stderr, "class %s: Memory leak found at %p with %lu B\n", m_strClassName.c_str(), ptr, sizeof(T));
ptr = ptr->pNext;
}
}
for (std::list<void *>::const_iterator it = m_lstBlockList.begin(); it != m_lstBlockList.end(); ++it)
{
free(*it);
}
}
template <typename T>
void* CMemoryPool<T>::Allocate()
{
CMutexLocker lock(&m_lock);
if (NULL == m_pFreeList)
{
if (!NeedMoreBlock())
{
return NULL;
}
}
SMemoryPoolNode<T> *pNode = m_pFreeList;
m_pFreeList = m_pFreeList->pNext;
if (m_pFreeList != NULL)
{
m_pFreeList->pPrev = NULL;
}
SMemoryPoolNode<T> *pLastUsedHead = m_pUsedList;
m_pUsedList = pNode;
m_pUsedList->pNext = pLastUsedHead;
if (pLastUsedHead != NULL)
{
pLastUsedHead->pPrev = m_pUsedList;
}
return &pNode->obj;
}
template <typename T>
void CMemoryPool<T>::Free(void *pBuffer)
{
CMutexLocker lock(&m_lock);
SMemoryPoolNode<T> *pNode = (SMemoryPoolNode<T> *)pBuffer;
if (pNode->pPrev != NULL)
{
pNode->pPrev->pNext = pNode->pNext;
if (pNode->pNext != NULL)
{
pNode->pNext->pPrev = pNode->pPrev;
}
}
else
{
m_pUsedList = pNode->pNext;
if (m_pUsedList != NULL)
{
m_pUsedList->pPrev = NULL;
}
}
pNode->pPrev = NULL;
SMemoryPoolNode<T> *pLastFreeHead = m_pFreeList;
m_pFreeList = pNode;
m_pFreeList->pNext = pLastFreeHead;
if (pLastFreeHead != NULL)
{
pLastFreeHead->pPrev = m_pFreeList;
}
}
template <typename T>
Bool CMemoryPool<T>::NeedMoreBlock()
{
void *ptr = malloc(m_nBlockSize);
if (NULL == ptr)
{
return False;
}
Int32 nNodeCount = m_nBlockSize / sizeof(SMemoryPoolNode<T>);
SMemoryPoolNode<T> *pLastNode = (SMemoryPoolNode<T> *)ptr;
m_pFreeList = pLastNode;
pLastNode->pPrev = NULL;
pLastNode->pNext = NULL;
for (Int32 i = 1; i < nNodeCount; ++i)
{
SMemoryPoolNode<T> *pNext = (SMemoryPoolNode<T> *)ptr + i;
pNext->pPrev = pLastNode;
pNext->pNext = NULL;
pLastNode->pNext = pNext;
pLastNode = pNext;
}
m_lstBlockList.push_back(ptr);
return True;
}
#endif
以上包含了 OSTypes.h, 其包含了類型自定義,同時(shí)為了在多線程中使用,使用了互斥鎖,并進(jìn)行了封裝。這二處的代碼略之。
測(cè)試代碼
#include <iostream>
#include <stdio.h>
#include "MemoryPool.h"
using namespace std;
class CMemoryTest
{
DECLARE_MEMORY_POOL(CMemoryTest)
public:
CMemoryTest()
{
}
~CMemoryTest()
{
}
private:
char m_szBuff[100];
};
IMPLEMENT_MEMORY_POOL(CMemoryTest)
Int32 main()
{
CMemoryTest *pTest1 = new CMemoryTest();
CMemoryTest *pTest2 = new CMemoryTest();
delete pTest1;
delete pTest2;
pTest1 = new CMemoryTest();
pTest2 = new CMemoryTest();
// delete pTest1;
// delete pTest2;
return 0;
}
測(cè)試結(jié)果如下:
class CMemoryTest: Memory leak found at 0x7f5d62518010 with 100 B
class CMemoryTest: Memory leak found at 0x7f5d62617f88 with 100 B
說明:
- 在定義 DECLARE_MEMORY_POOL 宏時(shí),使用 ## 將類名拼接到 m_memoryPool 的后面形成一個(gè)對(duì)象名稱;
- 在定義 IMPLEMENT_MEMORY_POOL 宏時(shí),為了給 CMemoryPool 的構(gòu)造函數(shù)傳遞類名字符串的參數(shù),使用 # 將宏參數(shù)轉(zhuǎn)換為字符串,在應(yīng)用程序退出時(shí),使用了內(nèi)存池的類的 m_memoryPool ## className 對(duì)象會(huì)被銷毀,在其析構(gòu)函數(shù)中,會(huì)將未釋放的內(nèi)存打印出來,包括類名、內(nèi)存地址及泄漏的字節(jié)數(shù),非常便于調(diào)試內(nèi)存泄漏。