C++内存池实现


内存池的实现:

  在堆内存中申请一大块内存当作内存模块,为了避免内存块的使用浪费,将内存模块根据所装载的内存块大小,分为不同的内存池。

定义一个结构体用来表示内存块信息,定义节点指针,方便内存块使用与归还,定义内存池标记表示是否属于内存池中的内存单元(当内存池中的内存单元使用完毕后,向系统申请的标记)。

//内存信息块的结构
typedef struct stMemoryBlock
{
	MemoryPool* pPoool;//当前的内存块属于哪一个内存池
	stMemoryBlock* pNext;//节点指针
	bool isInPool;//表示这个内存块是否属于池内存
}MEMORY_BLOCK, * LP_MEMORY_BLOCK;

内存池类的实现

//内存池类
class MemoryPool
{
	MemoryPool(const MemoryPool&) = delete;
	MemoryPool& operator = (const MemoryPool&) = delete;
public:
	MemoryPool();
	virtual ~MemoryPool();
	//初始化内存池
	bool InitPool();
	//对池子中申请内存
	void* MemAlloc(size_t size);
	//从池子中释放内存
	void MemFree(void* pMem);
protected:
private:
	char* _pPoolBuffer;//整个内存池的内存的指针,指向一大块堆内存
	LP_MEMORY_BLOCK _pHeader;//池子中内存块链表的头部
	size_t _nSize;//表示的是内存单元的大小
	size_t _nBlockCount;//内存单元数量
	std::mutex _mutex;  //预防在多线程中节点指针的使用
};

内存池的初始化申请内存 内存池链表进行连接

内存池当中的内存块大小 = 单元内存块大小+描述信息(结构体)

bool MemoryPool::InitPool()
{
    if (_pPoolBuffer) { return true; }//避免重复的初始化
    //计算出需要申请多少内存
    //真正的内存块的大小 = 单元内存块大小+描述信息(结构体)
    size_t realsize = _nSize + sizeof(MEMORY_BLOCK);
    //内存池的大小 = 真正的内存块的大小*内存块数量
    size_t bufsize = realsize * _nBlockCount;
    //一次性申请一大块内存池内存
    _pPoolBuffer = (char*)malloc(bufsize);
    if (_pPoolBuffer == nullptr)
    {
        return false;
    }
    //申请内存成功,可以开始构造内存块的链表
    //对池子的内存块的数据进行初始化
    _pHeader = (LP_MEMORY_BLOCK)_pPoolBuffer;
    _pHeader->isInPool = true;
    _pHeader->pPoool = this;
    _pHeader->pNext = nullptr;
    
    LP_MEMORY_BLOCK pPerBlock = _pHeader;
    LP_MEMORY_BLOCK pCurBlock = nullptr;
    for (size_t i = 1; i < _nBlockCount; ++i)
    {
        //每次循环移动到下一个内存块的首部
        pCurBlock = (LP_MEMORY_BLOCK)(_pPoolBuffer + (realsize * i));
        //更新新节点信息
        pCurBlock->isInPool = true;
        pCurBlock->pPoool = this;
        pCurBlock->pNext = nullptr;
        //将节点进行连接
        pPerBlock->pNext = pCurBlock;
        //更新节点指针指向
        pPerBlock = pCurBlock;

    }

    return true;
}

申请内存块实现

内存池中内存块耗尽后可以尝试从系统进行分配,为了方便管理需要加上信息块内存大小的偏移

申请返回出去的内存块 需要偏移去除掉信息块部分。

void* MemoryPool::MemAlloc(size_t size)
{
    //在操作链表的指针的这一部分我们需要保证多线程下的安全
    std::lock_guard<std::mutex>lock(_mutex);
    if (!_pHeader)
    {
        if (!InitPool())
        {
            return nullptr;
        }
    }
    LP_MEMORY_BLOCK pReturn = nullptr;
    //内存池中的内存块耗尽,可以尝试从系统中进行临时分配
    if (nullptr == _pHeader)
    {
        //系统分配后需要加上信息块部分方便管理
        pReturn = (LP_MEMORY_BLOCK)malloc(size + sizeof(MEMORY_BLOCK));
        pReturn->isInPool = false;
        pReturn->pNext = nullptr;
        pReturn->pPoool = nullptr;
    }
    else //池子中还有可以使用的内存
    {
        pReturn = _pHeader;
        _pHeader = _pHeader->pNext;
    }
    //返回出去的内存需要偏移去除信息块部分
    return ((char*)pReturn)+sizeof(MEMORY_BLOCK);
}

释放内存块

归还时需要偏移内存块信息大小,还原为管理中内存块的大小,如果内存块不是内存池中的而是系统分配的,直接释放。

void MemoryPool::MemFree(void* pMem)
{
    std::lock_guard<std::mutex>lock(_mutex);
    if (!pMem)return;
    //偏移信息块大小部分,还原为储存中内存块大小
    LP_MEMORY_BLOCK pBlock = (LP_MEMORY_BLOCK)(((char*)pMem) - sizeof(MEMORY_BLOCK));
    if (pBlock->isInPool)//如果是池子的内存可以还入链表
    {
        pBlock->pNext = _pHeader;
        _pHeader = pBlock;
    }
    else
    {
        //如果是从系统分配的
        free(pBlock);
    }

添加内存分配类,使用模板的方式传入内存块大小,和内存块个数

//内存池分配类,实际上继承的时内存池类,但是完善了对单元大小和内存块数量的初始化工作
template<size_t nSize,size_t nBlockCount>
class MemoryAlloctor :public MemoryPool
{
public:
	MemoryAlloctor()
	{
		//初始化单元大小和内存块数量
		const size_t n = sizeof(void*);//计算的是当前的情况指针的字节大小
		//为了让指针偏移效率更为高效,我们保证内存块的大小按照对齐的长度的整数倍进行划分
		_nSize = (nSize / n) * n + (nSize % n ? n : 0);
		_nBlockCount = nBlockCount;
	}
};

新增内存池管理,通过内存池管理提前分配好不同大小的内存池

//内存管理类(一个内存管理类可以包含多个内存池)
class MemoryMgr
{
private:
	MemoryMgr();
	MemoryMgr(const MemoryMgr&) = delete;
	MemoryMgr& operator = (const MemoryMgr&) = delete;
public:
	~MemoryMgr();
	static MemoryMgr& Instance();
	void* AllocMem(size_t size);
	void FreeMem(void* pMem);
protected:
	void _InitMemPool(int startindex,int endindex,MemoryPool*pPool);
	//管理分配内存池
	MemoryAlloctor<64, 100000> _mem64;  //建立了100000个64字节单元大小的内存池
	MemoryAlloctor<128, 100000> _mem128; //建立了100000个128字节单元大小的内存池
	MemoryAlloctor<256, 100000> _mem256; //建立了100000个256字节单元大小的内存池
	MemoryAlloctor<512, 100000> _mem512; //建立了100000个512字节单元大小的内存池

	MemoryPool* _pMemPools[512 + 1];//为了节省查询时间 提高空间消耗

};

内存池分配管理具体实现

MemoryMgr::MemoryMgr()
{
    _InitMemPool(0, 64, &_mem64);
    _InitMemPool(65, 128, &_mem128);
    _InitMemPool(129, 256, &_mem256);
    _InitMemPool(257, 512, &_mem512);
}

MemoryMgr::~MemoryMgr()
{
}

MemoryMgr& MemoryMgr::Instance()
{
    static MemoryMgr instance;
    return instance;
}

void* MemoryMgr::AllocMem(size_t size)
{
    if (size <= 512)
    {
        //做了申请大小和对应内存池的映射关系
        //直接可以根据下标来定位使用那一个内存池
        return _pMemPools[size]->MemAlloc(size);
    }
    //系统分配后需要加上信息块部分方便管理
    LP_MEMORY_BLOCK pReturn = nullptr;
    pReturn = (LP_MEMORY_BLOCK)malloc(size + sizeof(MEMORY_BLOCK));
    pReturn->isInPool = false;
    pReturn->pNext = nullptr;
    pReturn->pPoool = nullptr;
    return ((char*)pReturn) + sizeof(MEMORY_BLOCK);
}

void MemoryMgr::FreeMem(void* pMem)
{
    if (!pMem)
    {
        return;
    }

    LP_MEMORY_BLOCK pBlock = (LP_MEMORY_BLOCK)(((char*)pMem) - sizeof(MEMORY_BLOCK));
    if (pBlock->isInPool)//如果是池子的内存可以还入链表
    {
        pBlock->pPoool->MemFree(pMem);
        
    }
    else
    {
        free(pBlock);
    }

}

void MemoryMgr::_InitMemPool(int startindex, int endindex, MemoryPool* pPool)
{
    //以数组的方式建立了内存申请大小和内存池的联系
    for (int i = startindex; i <= endindex; ++i)
    {
        _pMemPools[i] = pPool;
    }

 

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/268309.html

(0)
上一篇 2022年6月19日
下一篇 2022年6月19日

相关推荐

发表回复

登录后才能评论