内存池的实现:
在堆内存中申请一大块内存当作内存模块,为了避免内存块的使用浪费,将内存模块根据所装载的内存块大小,分为不同的内存池。
定义一个结构体用来表示内存块信息,定义节点指针,方便内存块使用与归还,定义内存池标记表示是否属于内存池中的内存单元(当内存池中的内存单元使用完毕后,向系统申请的标记)。
//内存信息块的结构
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