内存池实现C/C++

内存池的实现 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字节)。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值