内存池的实现 C/C++
为什么要使用内存池
在实际开发中,我们经常需要使用new与delete去分配内存给我们的对象,但是随着程序并发数量的增加,new与delete使 用数量的增加,内存在不断的申请与释放后会产生无数内存碎片,导致计算机系统资源的浪费,这时候我们就可以只用内存池,每当程序需要申请内存一定数额的时候,我们创建的内存池就分配程序一个固定数额的内存(例如 64 个字节),这么做虽然会造成内存空间的浪费,但是减少了操作系统分配内存(最佳适应法等)所消耗的时间,有助于我们更好的管理内存空间,是典型的空间换取时间。
内存池的实现需要哪些知识
1.链表
2.new与delete重载,malloc与free
3.单例设计模式
内存池结构解析
程序实现分为四大块:
1.内存池类(包括不同申请内存函数与释放内存函数等)
2.内存池管理类(用于存放储存不同大小内存的内存池)
3.内存块的头部数据类
4.重载new与delete
MemoryMgr.cpp
#include <assert.h>
#include <stdlib.h>
#include <thread>
#include <mutex>
//最大的内存块大小
#define MAX_MEMORY_SIZE 128
#ifdef _DEBUG
#include<stdio.h>
#define xprintf(...) printf(__VA_ARGS__)
#else
#define xprintf(...)
#endif
class MemoryAlloc;
//内存块
class MemoryBlock
{
public:
MemoryBlock()
{
_pAlloc = nullptr;
_pNext = nullptr;
}
~MemoryBlock()
{
}
//内存块编号
int nID;
//引用次数
int nRef;
MemoryAlloc * _pAlloc;
MemoryBlock * _pNext;
bool bPool;
private:
char c1;
char c2;
char c3;
};
//内存池
class MemoryAlloc
{
public:
MemoryAlloc()
{
_pHeader = nullptr;
_pBuff = nullptr;
_nSize = 0;
_nBlockSize = 0;
}
~MemoryAlloc()
{
if (_pBuff)
free(_pBuff);
}
//申请内存
void *allocMemory(size_t nSize)
{
std::lock_guard<std::mutex> lock(_mutex);
if (!_pBuff)
{
InitMemory();
}
MemoryBlock *pReturn = nullptr;
if (_pHeader == nullptr) //申请内存数量超出内存池设定范围后,直接向操作系统申请
{
pReturn = reinterpret_cast<MemoryBlock *>(malloc(nSize+sizeof(MemoryBlock)));
pReturn->bPool = false;
pReturn->nID = -1;
pReturn->nRef = 1;
pReturn->_pAlloc = nullptr;
pReturn->_pNext = nullptr;
printf("allocMem: %llx,id = %d ,size = %d\n", pReturn, pReturn->nID, nSize);
}
else
{
pReturn = _pHeader;
_pHeader = _pHeader->_pNext;
assert(0 == pReturn->nRef);
pReturn->nRef = 1;
}
xprintf("allocMem: %llx,id = %d ,size = %d\n", pReturn, pReturn->nID, nSize);
return (((char *)pReturn) + sizeof(MemoryBlock)); //实际返回给用户的内存地址是内存池首地址+头部Block长度后的地址
}
//释放内存
void freeMemory(void *pMem)
{
MemoryBlock *pBlock = reinterpret_cast<MemoryBlock *>( (char *)pMem - sizeof(MemoryBlock) );
assert(1 == pBlock->nRef);
if (pBlock->bPool)
{
if (--pBlock->nRef != 0) //判断是否被调用过
{
return;
}
std::lock_guard<std::mutex> lock(_mutex);
pBlock->_pNext = _pHeader;
_pHeader = pBlock;
}
else
{
if (--pBlock->nRef != 0)
{
return;
}
//操作系统里面有做线程安全处理的
free(pBlock);
}
}
//初始化
void InitMemory()
{
//断言
assert(_pBuff == nullptr);
if (_pBuff)
{
return;
}
//向系统申请池内存
//计算内存池的大小
size_t buffSize = (_nSize + sizeof(MemoryBlock))* _nBlockSize;
size_t realSize = _nSize + sizeof(MemoryBlock);
//申请内存
_pBuff = reinterpret_cast<char *>(malloc(buffSize));
//初始化内存池
_pHeader = reinterpret_cast<MemoryBlock *>(_pBuff);
_pHeader->bPool = true;
_pHeader->nID = 0;
_pHeader->nRef = 0;
_pHeader->_pAlloc = this;
_pHeader->_pNext = nullptr;
MemoryBlock *pTemp1 = _pHeader;
for (size_t n = 1; n < _nBlockSize; n++)
{
MemoryBlock *pTemp2 = reinterpret_cast<MemoryBlock*>(_pBuff + (n*realSize));
pTemp2->bPool = true;
pTemp2->nID = n;
pTemp2->nRef = 0;
pTemp2->_pAlloc = this;
pTemp1->_pNext = pTemp2;
pTemp2->_pNext = nullptr;
pTemp1 = pTemp2;
}
}
protected:
//内存池地址
char *_pBuff;
//头部内存单元
MemoryBlock *_pHeader;
//内存单元大小
size_t _nSize;
//内存块数量
size_t _nBlockSize;
std::mutex _mutex;
};
template<size_t nSize, size_t nBlockSize>
class MemoryAlloctor :public MemoryAlloc
{
public:
//保证初始化 _nSize 最终传入值是8的倍数
MemoryAlloctor()
{
const size_t n = sizeof(void *); // n = 8
_nSize = (nSize / n)*n + (_nSize % n ? n : 0);
_nBlockSize = nBlockSize;
}
};
//内存池管理
class MemoryMgr
{
private:
MemoryMgr()
{
init_szAlloc(0, 64, &_mem64); //创建内存大小为64K的内存池
init_szAlloc(65, 128, &_mem128); //创建内大小为128K的内存池
//init_szAlloc(129, 256, &_mem256);
//init_szAlloc(257, 512, &_mem512);
//init_szAlloc(513, 1024, &_mem1024);
}
~MemoryMgr() {}
public:
//单例模式下获取对象的方法
static MemoryMgr& Instance()
{
//单例设计模式确保内存池管理类只有一个实例
static MemoryMgr mgr;
return mgr;
}
//申请内存
void *allocMem(size_t nSize)
{
if (nSize <= MAX_MEMORY_SIZE)
{
return _szAlloc[nSize]->allocMemory(nSize);
}
else
{
MemoryBlock *pReturn = reinterpret_cast<MemoryBlock *>(malloc(nSize + sizeof(MemoryBlock)));
pReturn->bPool = false;
pReturn->nID = -1;
pReturn->nRef = 1;
pReturn->_pAlloc = nullptr;
pReturn->_pNext = nullptr;
xprintf("allocMem: %llx,id = %d ,size = %d\n", pReturn, pReturn->nID, nSize);
return (((char *)pReturn) + sizeof(MemoryBlock));
}
}
//释放内存
void freeMem(void *pMem)
{
MemoryBlock *pBlock = reinterpret_cast<MemoryBlock *>((char *)pMem - sizeof(MemoryBlock));
xprintf("freeMem: %llx,id = %d \n", pBlock, pBlock->nID);
if (pBlock->bPool)
{
pBlock->_pAlloc->freeMemory(pMem);
}
else
{
if (--pBlock->nRef == 0)
{
free(pBlock);
}
}
}
private:
//初始化内存池映射数组(告诉MemoryMgr应该去哪个内存池申请内存)
void init_szAlloc(int nBegin,int nEnd,MemoryAlloc *pMem)
{
for (int n = nBegin; n <= nEnd; n++)
{
_szAlloc[n] = pMem;
}
}
private:
MemoryAlloctor<64, 4000000>_mem64; // 64K 以内内存在此申请
MemoryAlloctor<128, 1000000>_mem128; // 65k-128k内存在此申请
//MemoryAlloctor<256, 1100>_mem256;
//MemoryAlloctor<512, 1100>_mem512;
//MemoryAlloctor<1024, 1100>_mem1024;
MemoryAlloc *_szAlloc[MAX_MEMORY_SIZE + 1]; //内存映射数组
};
Alloctor.h 声明函数
void *operator new(size_t size);
void *operator new[](size_t size);
void operator delete[](void *p);
void operator delete(void* p);
void *mem_alloc(size_t size);
void mem_free(void *p);
Alloctor.cpp 重载new与delete
#include "Alloctor.h"
#include "MemoryMgr.hpp"
void *operator new(size_t size)
{
return MemoryMgr::Instance().allocMem(size);
}
void *operator new [](size_t size)
{
return MemoryMgr::Instance().allocMem(size);
}
void operator delete(void *p)
{
MemoryMgr::Instance().freeMem(p);
}
void operator delete[](void* p)
{
MemoryMgr::Instance().freeMem(p);
}
void *mem_alloc(size_t size)
{
return malloc(size);
}
void mem_free(void *p)
{
free(p);
}
测试
#include "Alloctor.h"
#include<time.h>
#include<iostream>
#include <stdlib.h>
int main()
{
clock_t start, end;
start = clock();
char *data[10];
for (size_t n = 0; n < 10; n++)
{
data[n] = new char[n + 1];
}
for (size_t n = 0; n < 10; n++)
{
delete[] data[n];
}
end = clock();
std::cout << "运行时间" << (double)(end - start) / CLOCKS_PER_SEC << std::endl;
return 0;
}
可见,每个内存块地址相差0x60,等于内存块头部MemoryBlock (32字节+64字节)。