在需要频繁地 new/delete 对象时,很容易造成对象分配慢、内存碎片的产生。为提升应用程序在分配对象内存的效率,可以使用内存池管理对象内存的分配和释放。
作者按以下方式实现:
- 编写模板类 CMemoryPool, 为每个类创建一个静态的内存池对象
- 内存池类按大块向系统申请内存,将内存以静态双向链表进行关联,形成空闲链表
- 分配类对象内存时,从空闲链表淘汰一个元素,并将此结点添加到已分配的双向链表中
- 在内存池中将分配过的内存结点使用双向链表关联,同时实现应用程序退出后内存泄漏自动检查
- 释放类对象内存时,从已分配的双向链表中移除结点元素,并归还给空闲链表中
- 提供宏为类添加内存池的静态成员对象,并重载类的 operator new 和 operator delete 运算符
- 不允许构造对象数组(此内存池内不支持动态创建对象数组)
- 在使用时,只需要在类定义中加入 DECLARE_MEMORY_POOL 宏声明,在实现文件中加入 IMPLEMENT_MEMORY_POOL 宏实现
MemoryPool.h 文件中实现了模板类 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;
};
// 定义内存池块的大小
#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()
{
// 打印出未释放的内存
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, 其包含了类型自定义,同时为了在多线程中使用,使用了互斥锁,并进行了封装。这二处的代码略之。
测试代码
#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;
}
测试结果如下:
class CMemoryTest: Memory leak found at 0x7f5d62518010 with 100 B
class CMemoryTest: Memory leak found at 0x7f5d62617f88 with 100 B
说明:
- 在定义 DECLARE_MEMORY_POOL 宏时,使用 ## 将类名拼接到 m_memoryPool 的后面形成一个对象名称;
- 在定义 IMPLEMENT_MEMORY_POOL 宏时,为了给 CMemoryPool 的构造函数传递类名字符串的参数,使用 # 将宏参数转换为字符串,在应用程序退出时,使用了内存池的类的 m_memoryPool ## className 对象会被销毁,在其析构函数中,会将未释放的内存打印出来,包括类名、内存地址及泄漏的字节数,非常便于调试内存泄漏。