使用内存池管理对象内存

在需要频繁地 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 
#include 
#include 
#include 
 
#define DECLARE_MEMORY_POOL(className) \
    private: \
        static CMemoryPool 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::m_memoryPool ## className(#className);
 
template 
struct SMemoryPoolNode
{
    T                       obj;
    SMemoryPoolNode*        pPrev;
    SMemoryPoolNode*        pNext;
};
 
// 定义内存池块的大小
#define MEMORY_POOL_BLOCK_SIZE 1048576LL
 
template 
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                m_lstBlockList;
 
    SMemoryPoolNode*               m_pFreeList;
    SMemoryPoolNode*               m_pUsedList;
    CMutex                          m_lock;
};
 
template 
CMemoryPool::CMemoryPool(LPCSTR lpszClassName) : m_strClassName(lpszClassName)
{
    m_nBlockSize = MEMORY_POOL_BLOCK_SIZE;
    m_nBlockSize = m_nBlockSize / sizeof(SMemoryPoolNode) * sizeof(SMemoryPoolNode);
    if (sizeof(SMemoryPoolNode) > m_nBlockSize)
    {
        m_nBlockSize = 10 * sizeof(SMemoryPoolNode);
    }
 
    m_pFreeList = NULL;
    m_pUsedList = NULL;
 
    NeedMoreBlock();
}
 
template 
CMemoryPool::~CMemoryPool()
{
    // 打印出未释放的内存
    if (m_pUsedList != NULL)
    {
        SMemoryPoolNode *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::const_iterator it = m_lstBlockList.begin(); it != m_lstBlockList.end(); ++it)
    {
        free(*it);
    }
}
 
template 
void* CMemoryPool::Allocate()
{
    CMutexLocker lock(&m_lock);
 
    if (NULL == m_pFreeList)
    {
        if (!NeedMoreBlock())
        {
            return NULL;
        }
    }
 
    SMemoryPoolNode *pNode = m_pFreeList;
    m_pFreeList = m_pFreeList->pNext;
 
    if (m_pFreeList != NULL)
    {
        m_pFreeList->pPrev = NULL;
    }
 
    SMemoryPoolNode *pLastUsedHead = m_pUsedList;
    m_pUsedList = pNode;
    m_pUsedList->pNext = pLastUsedHead;
    if (pLastUsedHead != NULL)
    {
        pLastUsedHead->pPrev = m_pUsedList;
    }
 
    return &pNode->obj;
}
 
template 
void CMemoryPool::Free(void *pBuffer)
{
    CMutexLocker lock(&m_lock);
 
    SMemoryPoolNode *pNode = (SMemoryPoolNode *)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 *pLastFreeHead = m_pFreeList;
    m_pFreeList = pNode;
    m_pFreeList->pNext = pLastFreeHead;
    if (pLastFreeHead != NULL)
    {
        pLastFreeHead->pPrev = m_pFreeList;
    }
}
 
template 
Bool CMemoryPool::NeedMoreBlock()
{
    void *ptr = malloc(m_nBlockSize);
    if (NULL == ptr)
    {
        return False;
    }
 
    Int32 nNodeCount = m_nBlockSize / sizeof(SMemoryPoolNode);
    SMemoryPoolNode *pLastNode = (SMemoryPoolNode *)ptr;
 
    m_pFreeList = pLastNode;
    pLastNode->pPrev = NULL;
    pLastNode->pNext = NULL;
 
    for (Int32 i = 1; i < nNodeCount; ++i)
    {
        SMemoryPoolNode *pNext = (SMemoryPoolNode *)ptr + i;
        pNext->pPrev = pLastNode;
        pNext->pNext = NULL;
        pLastNode->pNext = pNext;
        pLastNode = pNext;
    }
 
    m_lstBlockList.push_back(ptr);
    return True;
}
 
#endif

以上包含了 OSTypes.h, 其包含了类型自定义,同时为了在多线程中使用,使用了互斥锁,并进行了封装。这二处的代码略之。

测试代码
#include 
#include 
#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 对象会被销毁,在其析构函数中,会将未释放的内存打印出来,包括类名、内存地址及泄漏的字节数,非常便于调试内存泄漏。

你可能感兴趣的:(使用内存池管理对象内存)