内存池的实现

相对于在栈空间分配内存,堆中分配内存其实是非常缓慢的。

另外,由于堆中分配的内存,需要开发者编码回收,当系统非常庞大时,容易出现分配的内容没有回收导致内存泄露的现象。

因此,许多Bible建议开发者尽量使用栈空间,少用甚至不用malloc和free、new和delete;

虽然栈的空间较小,但这样的建议随着计算机的位数从32位升级到64位,越来越成为真理。

但我还是想说,这是有限制的:那就是这条真理适用于多线程程序,但在多协程程序中,由于协程栈空间的限制,极容易撑爆协程栈。

为了提高堆分配内存的速度,内存池出现了。

下面提供固定大小的内存池和可变大小的内存池实现。经过测试,它的性能远高于Boost的内存池哦!

基础头文件base.h

#ifndef __PP_BASE_H__
#define __PP_BASE_H__

#if (defined(WIN32) || defined(WIN64))

#ifndef WINVER                          // Specifies that the minimum required platform is Windows Vista.
#define WINVER 0x0600           // Change this to the appropriate value to target other versions of Windows.
#endif

#ifndef _WIN32_WINNT            // Specifies that the minimum required platform is Windows Vista.
#define _WIN32_WINNT 0x0600     // Change this to the appropriate value to target other versions of Windows.
#endif

#ifndef _WIN32_WINDOWS          // Specifies that the minimum required platform is Windows 98.
#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
#endif

#ifndef _WIN32_IE                       // Specifies that the minimum required platform is Internet Explorer 7.0.
#define _WIN32_IE 0x0700        // Change this to the appropriate value to target other versions of IE.
#endif

//

#define WIN32_LEAN_AND_MEAN             // Exclude rarely-used stuff from Windows headers
// Windows Header Files:

#include <winsock2.h>
#include <windows.h>
#ifndef PPAPI
#define PPAPI __stdcall
#endif

#else

#ifndef PPAPI
#define PPAPI
#endif

#endif


#endif

内存池头文件

#ifndef __PPMEMORYPOOL_H__
#define __PPMEMORYPOOL_H__

#include "ppbase.h"

/**
* @brief 可变内存块大小内存池,
* 内存池申请的内存不会自动释放,只在内存池销毁时释放。
* 注意:此类“非”线程安全
* 使用示例:
* CVarMemoryPool pool;
* pool.Create();
* 某一线程:
* char * p1 = pool.Malloc(512);//记得加锁
* char * p2 = pool.Malloc(31);//记得加锁
* char * p3 = pool.Malloc(128);//记得加锁
*
* 另外一个线程:
* pool.Free(p3);//记得加锁
* pool.Free(p2);//记得加锁
* pool.Free(p1);//记得加锁
*/
class CVarMemoryPool
{
    struct MemoryPage
    {
        MemoryPage* Next;	// next memory page
    };

public:
    CVarMemoryPool();
    ~CVarMemoryPool();

    /**
    * @brief
    * 创建可变内存池
    * @param dwPageSize : 内部分配的内存页大小,内存不够时,内存池会申请一块新的内存页
    * @return 创建成功返回TRUE,否则返回FALSE
    **/
    bool PPAPI Create(unsigned int dwPageSize = 0x80000);

    /**
    * @brief
    * 分配Len长度的Buffer
    * @param dwLen : 获得的内存块大小长度
    * @return  返回的内存,如果返回为NULL,则代表分配失败
    **/
    void* PPAPI Malloc(unsigned int dwLen);

    /**
    * @brief
    * 回收内存
    * @param p : 指向需要回收的内存
    * @return void
    **/
    void PPAPI Free(void* p);

    /**
    * @brief
    * 清空内存池,使所有的内存都可以使用,此方法不会将内存返回给操作系统
    * @return void
    **/
    void PPAPI Clear();

    /**
    * @brief
    * 获取当前内存使用量
    * @return 当前内存使用量
    **/
    int PPAPI GetMemUsed();

private:
    void* GetPoolMemory(unsigned int dwLen);
    void FreePoolMemory(void* pMemBlock, unsigned char dwType);

    bool AddFreeMemory(int dwIndex);
    bool SetMemoryPage();

    inline char* GetPageBufGegin(MemoryPage *pPage)
    {
        return (char*)(pPage + 1);
    }

    inline char* GetPageBufEnd(MemoryPage *pPage)
    {
        return (char*)(pPage + 1) + m_nPageSize;
    }

private:
    static const unsigned int ALIGNMENT = 8;
    static const unsigned int ALLOC_COUNT = 16;
    static const unsigned int MIN_PAGESIZE = 0x40000;	// min pPage size
    static const unsigned int MAX_UNIT_SIZE = 128;
    static const unsigned int UNIT_TYPE_COUNT = 16;

    char* m_pFreeHead[UNIT_TYPE_COUNT];
    int m_nFreeCount[UNIT_TYPE_COUNT];

    MemoryPage* m_pHeadPage;
    MemoryPage* m_pWorkPage;
    char* m_pPageBuf;
    unsigned int m_nPageSize;
};




/**
*@brief 固定内存块大小内存池,用于分配固定大小的内存块
* 内存池申请的内存不会自动释放,只在内存池销毁时释放
* 注意:此类“非”线程安全
* 使用示例:
* CFixMemoryPool pool;
* pool.Create(128);
* 某一线程:
* char * p1 = pool.Malloc();//记得加锁
* char * p2 = pool.Malloc();//记得加锁
* char * p3 = pool.Malloc();//记得加锁
*
* 另外一个线程:
* pool.Free(p3);//记得加锁
* pool.Free(p2);//记得加锁
* pool.Free(p1);//记得加锁
*/
class CFixMemoryPool
{
    struct MemoryPage
    {
        MemoryPage* Next;	// next memory page
        int nFreeHead;		// the first free unit in page
        int nFreecount;		// free unit in page
    };

public:
    CFixMemoryPool();
    ~CFixMemoryPool();

    /**
    * @brief
    * 初始化内存池
    * @param dwUnitSize : 每一个分配的内存块大小
    * @param dwPageSize : 内部分配的内存页大小,内存不够时,内存池会申请一块新的内存页
    * @return  创建成功返回true,失败返回false
    **/
    bool PPAPI Create(unsigned int dwUnitSize, unsigned int dwPageSize = 0x40000);

    /**
    * @brief
    * 得到一块新的内存
    * @return  void*
    **/
    void* PPAPI Malloc();

    /**
    * @brief
    * 归还一块分配的内存
    * @param p : 内存的地址
    * @return  void
    **/
    void PPAPI Free(void* p);

    /**
    * @brief
    * 清空内存池,使所有的内存都可以使用,此方法不会将内存返回给操作系统
    * @return void
    **/
    void PPAPI Clear();

    /**
    * @brief
    * 获取当前内存使用量
    * @return 当前内存使用量
    **/
    int PPAPI GetMemUsed();

private:
    bool AddMemoryPage();
    void InitPage(MemoryPage *pPage);

    inline char* GetPageBuf(MemoryPage *pPage)
    {
        return (char*)(pPage + 1);
    }

private:
    static const int ALIGNMENT = 4;
    static const unsigned int MIN_PAGESIZE = 0x40000;	// min page size

    MemoryPage* m_pHeadPage;					// first page
    unsigned int m_nUnitSize;					// unit memory size
    unsigned int m_nPageSize;					// total memory in page
};

#endif

内存池cpp文件

#include "memorypool.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

CVarMemoryPool::CVarMemoryPool()
    :m_pHeadPage(NULL), m_pWorkPage(NULL), m_pPageBuf(NULL)
{
    for (unsigned int i = 0; i < UNIT_TYPE_COUNT; ++ i)
    {
        m_pFreeHead[i] = NULL;
        m_nFreeCount[i] = 0;
    }
}

CVarMemoryPool::~CVarMemoryPool()
{
    MemoryPage* pMemoryPage = m_pHeadPage;
    while (m_pHeadPage != NULL)
    {
        pMemoryPage = m_pHeadPage->Next;
        free(m_pHeadPage);
        m_pHeadPage = pMemoryPage;
    }
}

void* CVarMemoryPool::Malloc(unsigned int Len)
{
    assert(Len > 0);

    Len ++;
    if (Len > MAX_UNIT_SIZE)
    {
        // allocate memory from system if requery Len is too large
        void* buf = malloc(Len);
        if (buf == NULL)
        {
            return NULL;
        }

        //if content of 1 byte before memory means allocate form system
        *(char*)buf = 0;

        return (char*)buf + 1;
    }
    else
    {
        return GetPoolMemory(Len);
    }
}

void CVarMemoryPool::Free(void* p)
{
    assert(p != NULL);

    char* temp = (char*)p - 1;
    unsigned char type = *temp;
    if (type == 0)	//if content of 1 byte before memory means allocate form system
    {
        free(temp);
    }
    else
    {
        FreePoolMemory(temp, type);
    }
}

void* CVarMemoryPool::GetPoolMemory(unsigned int Len)
{
    Len = (Len + (ALIGNMENT-1)) & ~(ALIGNMENT-1);
    int idx = (Len - 1) / ALIGNMENT;

    //if free memory unit is not enough, first get some free units
    if (m_nFreeCount[idx] == 0
            && !AddFreeMemory(idx))
    {
        return NULL;
    }

    -- m_nFreeCount[idx];
    char* buf = m_pFreeHead[idx];
    m_pFreeHead[idx] = (char*)(*((INT64*)m_pFreeHead[idx]));
    *buf = idx + 1;

    return buf + 1;
}

void CVarMemoryPool::FreePoolMemory(void* memblock, unsigned char type)
{
    int idx = type - 1;
    *(INT64*)memblock = (INT64)m_pFreeHead[idx];
    m_pFreeHead[idx] = (char*)memblock;
    ++ m_nFreeCount[idx];
}

bool CVarMemoryPool::AddFreeMemory(int idx)
{
    const int UNIT_SIZE = (idx + 1) * ALIGNMENT;

    if ((m_pPageBuf + UNIT_SIZE ) > GetPageBufEnd(m_pWorkPage)
            && !SetMemoryPage())
    {
        return false;
    }

    char* page_end = GetPageBufEnd(m_pWorkPage);
    for (unsigned int i = 0; i < ALLOC_COUNT; ++ i)
    {
        *(INT64*)m_pPageBuf = (INT64)m_pFreeHead[idx];
        m_pFreeHead[idx] = m_pPageBuf;

        m_pPageBuf += UNIT_SIZE;
        ++ m_nFreeCount[idx];

        if (m_pPageBuf + UNIT_SIZE > page_end)
            break;
    }

    return true;
}

bool CVarMemoryPool::SetMemoryPage()
{
    if(m_pWorkPage->Next != NULL)
    {
        m_pWorkPage = m_pWorkPage->Next;
    }
    else
    {
        void* buf = malloc(sizeof(MemoryPage) + m_nPageSize);
        if (buf == NULL)
        {
            return false;
        }
        else
        {
            MemoryPage* pMemoryPage = (MemoryPage*)(buf);
            pMemoryPage->Next = NULL;
            m_pWorkPage->Next = pMemoryPage;
            m_pWorkPage = pMemoryPage;
        }
    }
    m_pPageBuf = GetPageBufGegin(m_pWorkPage);
    return true;
}

int CVarMemoryPool::GetMemUsed()
{
    int used = 0;
    const int PAGE_SIZE = sizeof(MemoryPage) + m_nPageSize;

    MemoryPage* pMemoryPage = m_pHeadPage;
    while (pMemoryPage != NULL)
    {
        pMemoryPage = pMemoryPage->Next;
        used += PAGE_SIZE;
    }

    return used;
}

void CVarMemoryPool::Clear()
{
    m_pWorkPage = m_pHeadPage;
    m_pPageBuf = GetPageBufGegin(m_pWorkPage);
}

bool CVarMemoryPool::Create( unsigned int PageSize /*= 0x80000*/ )
{
    PageSize = (PageSize + (ALIGNMENT-1)) & ~(ALIGNMENT-1);
    if (PageSize < MIN_PAGESIZE)
    {
        m_nPageSize = MIN_PAGESIZE;
    }
    else
    {
        m_nPageSize = PageSize;
    }

    void* buf = malloc(sizeof(MemoryPage) + m_nPageSize);
    if (buf == NULL)
    {
        return false;
    }
    else
    {
        MemoryPage* pMemoryPage = (MemoryPage*)(buf);
        pMemoryPage->Next = NULL;
        m_pWorkPage = pMemoryPage;
        m_pPageBuf = GetPageBufGegin(m_pWorkPage);
        m_pHeadPage = m_pWorkPage;
    }

    return true;
}

//

CFixMemoryPool::CFixMemoryPool()
    :m_pHeadPage(NULL), m_nUnitSize(0), m_nPageSize(0)
{
}

CFixMemoryPool::~CFixMemoryPool()
{
    MemoryPage* pMemoryPage = m_pHeadPage;
    while (m_pHeadPage != NULL)
    {
        pMemoryPage = m_pHeadPage->Next;
        free(m_pHeadPage);
        m_pHeadPage = pMemoryPage;
    }
}

void* CFixMemoryPool::Malloc()
{
    MemoryPage* pMemoryPage = m_pHeadPage;
    while (pMemoryPage != NULL && pMemoryPage->nFreecount == 0)
    {
        pMemoryPage = pMemoryPage->Next;
    }

    // add new page if space is not enough
    if (pMemoryPage == NULL)
    {
        if(!AddMemoryPage())
        {
            return NULL;
        }
        pMemoryPage = m_pHeadPage;
    }

    // get unused memory
    -- pMemoryPage->nFreecount;
    char* buf = GetPageBuf(pMemoryPage) + pMemoryPage->nFreeHead * m_nUnitSize;
    pMemoryPage->nFreeHead = *(int*)(buf);

    return buf;
}

void CFixMemoryPool::Free(void* p)
{
    // don't check null point for fast
    MemoryPage* pMemoryPage = m_pHeadPage;
    char* buf = GetPageBuf(m_pHeadPage);

    // find point in which page
    while((p < buf ||
            p > buf + m_nPageSize) &&
            pMemoryPage != NULL)
    {
        pMemoryPage = pMemoryPage->Next;
        buf = GetPageBuf(pMemoryPage);
    }

    // do not in any page
    if (pMemoryPage == NULL)
    {
        return;
    }

    *(int*)p = pMemoryPage->nFreeHead;
    pMemoryPage->nFreeHead = ((char*)p - buf) / m_nUnitSize;
    ++ pMemoryPage->nFreecount;

    return;
}

bool CFixMemoryPool::AddMemoryPage()
{
    void* buf = malloc(sizeof(MemoryPage) + m_nPageSize);
    if (buf == NULL)
    {
        return false;
    }

    MemoryPage* pMemoryPage = (MemoryPage*)(buf);
    InitPage(pMemoryPage);

    if (m_pHeadPage == NULL)
    {
        pMemoryPage->Next = NULL;
        m_pHeadPage = pMemoryPage;
    }
    else
    {
        pMemoryPage->Next = m_pHeadPage;
        m_pHeadPage = pMemoryPage;
    }

    return true;
}

int CFixMemoryPool::GetMemUsed()
{
    int used = 0;
    const int PAGE_SIZE = sizeof(MemoryPage) + m_nPageSize;

    MemoryPage* pMemoryPage = m_pHeadPage;
    while (pMemoryPage != NULL)
    {
        pMemoryPage = pMemoryPage->Next;
        used += PAGE_SIZE;
    }

    return used;
}

void CFixMemoryPool::Clear()
{
    MemoryPage* pMemoryPage = m_pHeadPage;
    while (pMemoryPage != NULL)
    {
        InitPage(pMemoryPage);
        pMemoryPage = pMemoryPage->Next;
    }
}

void CFixMemoryPool::InitPage(MemoryPage *Page)
{
    Page->nFreecount = m_nPageSize / m_nUnitSize;
    Page->nFreeHead = 0;

    void* head = GetPageBuf(Page);
    for (int i = 1; i < Page->nFreecount; ++i)
    {
        *(int*)head = i;
        head = (int*)((char*)head + m_nUnitSize);
    }
}

bool CFixMemoryPool::Create( unsigned int UnitSize, unsigned int PageSize /*= 0x40000*/ )
{
    if (UnitSize < 4)
    {
        m_nUnitSize = 4;
    }
    {
        m_nUnitSize = (UnitSize + (ALIGNMENT-1)) & ~(ALIGNMENT-1);
    }

    if (PageSize < MIN_PAGESIZE)
    {
        m_nPageSize = MIN_PAGESIZE;
    }
    else
    {
        m_nPageSize = (PageSize / m_nUnitSize) * m_nUnitSize;
    }

    return AddMemoryPage();
}


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值