c++内存池实现

利用C/C++开发大型应用程序中,内存的管理与分配是一个需要认真考虑的部分。

本文描述了内存池设计原理并给出内存池的实现代码,代码支持Windows和Linux,多线程安全。

内存池设计过程中需要考虑好内存的分配与释放问题,其实也就是空间和时间的矛盾。

有的内存池设计得很巧妙,内存分配与需求相当,但是会浪费过多的时间去查找分配与释放,这就得不偿失;

实际使用中,我们更多的是关心内存分配的速度,而不是内存的使用效率。基于此,本文按照如下思想设计实现内存池。

主要包含三个结构:StiaticMemory, MemoryChunk和MemoryBlock,三者之间的关系如下图所示:



1.内存的分配:

(1)如果分配大小超过1024,直接采用malloc分配,分配的时候多分配sizeof(size_t)字节,用于保存该块的大小;

(2)否则根据分配大小,查找到容纳该大小的最小size的MemoryChunk;

(3)查找MemoryChunk的链表指针pList,找到空闲的MemoryBlock返回;

(4)如果pList为NULL,临时创建MemoryBlock返回;

(5)MemoryBlock头部包含两个成员,pChunk指向的所属的MemoryChunk对象,size表明大小,其后才是给用户使用的空间;

2.内存的释放:

(1)根据释放的指针,查找器size头部,即减去sizeof(size_t)字节,判断该块的大小;

(2)如果大小超过1024,直接free;

(3)否则交给MemoryChunk处理,而块的头部保存了该指针,因此直接利用该指针就可以收回该内存。

注意的问题:

上述设计的内存池通过冗余的头部来实现内存块的分配与释放,减少了内存池的操作时间,速度上要优于原始的malloc和free操作,同时减少了内存碎片的增加。

但是该设计中没有去验证释放的块冗余头部的正确性,因此故意释放不属于内存池中的块或者修改头部信息都会导致内存池操作失败,当然这些可以由程序员来控制。

此外,内存池中分配出去的内存块如果不主动释放,内存池没有保留信息,不会自动释放,但是在退出的时候会验证验证是否完全释放,其实这个在系统测试时候就可以检测出来,我想这个缺陷也是可以弥补的,在此提出,希望使用者注意。

下面贴上源码,如果对代码有任何建议或者发现存在的Bug,希望与我联系,共同学习交流,Tx。


MemoryChunk.h 文件,线程安全

#ifndef MEMORY_CHUNK_H
#define MEMORY_CHUNK_H
#include <cstdio>
#include <cassert>
#include <cstdlib>

#ifdef WIN32
#include <windows.h>
typedef CRITICAL_SECTION MUTEXTYPE;
#define INITMUTEX(hMutex) InitializeCriticalSection(&hMutex)
#define DELMUTEX(hMutex) DeleteCriticalSection(&hMutex)
#define LOCK(hMutex) EnterCriticalSection(&hMutex)
#define UNLOCK(hMutex) LeaveCriticalSection(&hMutex)
#else
#include <pthread.h>
typedef pthread_mutex_t MUTEXTYPE;
#define INITMUTEX(hMutex) pthread_mutex_init(&hMutex,NULL)
#define DELMUTEX(hMutex) pthread_mutex_destroy(&hMutex)
#define LOCK(hMutex) pthread_mutex_lock(&hMutex)
#define UNLOCK(hMutex) pthread_mutex_unlock(&hMutex)
#endif

class MemoryChunk;

/** @struct MemoryBlock
 *
 */
struct BlockHeader
{
	MemoryChunk* pChunk;
	size_t len;
};
struct MemoryBlock;
struct BlockData
{
	union{
		MemoryBlock* pNext;
		char pBuffer;
	};
};
struct MemoryBlock
{
	BlockHeader header;
	BlockData data;
};

/** @class MemoryChunk
 *
 */

class MemoryChunk
{
public:
	MemoryChunk(size_t size, int count)
	{
		INITMUTEX(hMutex);
		this->pFreeList=NULL;
		this->size=size;
		this->count=0;
		MemoryBlock* pBlock;
		while(count--){
			pBlock=CreateBlock();
			if(!pBlock)break;
			pBlock->data.pNext=pFreeList;
			pFreeList=pBlock;
		}
	}
	~MemoryChunk()
	{
		int tempcount=0;
		MemoryBlock* pBlock;
		while(pBlock=pFreeList){
			pFreeList=pBlock->data.pNext;
			DeleteBlock(pBlock);
			++tempcount;
		}
		assert(tempcount==count);//!确保释放完全
		DELMUTEX(hMutex);
	}
	void* malloc()
	{
		MemoryBlock* pBlock;
		LOCK(hMutex);
		if(pFreeList){
			pBlock=pFreeList;
			pFreeList=pBlock->data.pNext;
		}
		else{
			if(!(pBlock=CreateBlock())){
				UNLOCK(hMutex);
				return NULL;
			}
		}
		UNLOCK(hMutex);
		return &pBlock->data.pBuffer;
	}
	static void free(void* pMem)
	{
		MemoryBlock* pBlock=(MemoryBlock*)((char*)pMem-sizeof(BlockHeader));
		pBlock->header.pChunk->free(pBlock);
	}
	void free(MemoryBlock* pBlock)
	{
		LOCK(hMutex);
		pBlock->data.pNext=pFreeList;
		pFreeList=pBlock;
		UNLOCK(hMutex);
	}
	
	MemoryChunk* Next(){return pNext;}

protected:
	MemoryBlock* CreateBlock()
	{
		MemoryBlock* pBlock=(MemoryBlock*)::malloc(sizeof(BlockHeader)+size);

		if(pBlock){

			pBlock->header.pChunk=this;
			pBlock->header.len=size;
			
			++count;
		}
		return pBlock;
	}
	void DeleteBlock(MemoryBlock* pBlock)
	{
		::free(pBlock);
	}
private:
	MemoryBlock* pFreeList;
	size_t size;//!Block大小
	int count;//!Block数目
	MemoryChunk* pNext;
	MUTEXTYPE hMutex;
};
#endif
StaticMemory.h文件,内存池对象
#ifndef STATIC_MEMORY_H
#define STATIC_MEMORY_H
#include "MemoryChunk.h"
/** @ StaticMemory.h
 * 定义实现内存池
 * 采用固定大小策略进行内存管理与分配
 * 减少因大量小内存分配导致的内存碎片增加
 */
struct HeapHeader
{
	size_t size;
};
struct MemoryHeap
{
	HeapHeader header;
	char pBuffer;
};

class StaticMemory
{
public:
	typedef enum{MAX_SIZE=1024,MIN_SIZE=sizeof(MemoryChunk*)};
	StaticMemory()
	{
		chunkcount=0;
		for(size_t size=MIN_SIZE; size<=MAX_SIZE; size*=2)++chunkcount;
		//pChunkList=(MemoryChunk**)malloc(sizeof(MemoryChunk*)*chunkcount);
		pChunkList=new MemoryChunk*[chunkcount];
		int index=0;
		for(size_t size=MIN_SIZE; size<=MAX_SIZE; size*=2)
		{
			pChunkList[index++]=new MemoryChunk(size,1000);
		}
	}
	~StaticMemory()
	{
		for(int index=0; index<chunkcount; ++index)
		{
			delete pChunkList[index];
		}
		//free(pChunkList);
		delete[] pChunkList;
	}
	void* Malloc(size_t size)
	{
		if(size>MAX_SIZE){
			return malloc(size);
		}
		int index=0;
		for(size_t tsize=MIN_SIZE; tsize<=MAX_SIZE; tsize*=2){
			if(tsize>=size)break;
			++index;
		}
		return pChunkList[index]->malloc();
	}
	void Free(void* pMem)
	{
		if(!free(pMem))MemoryChunk::free(pMem);
	}
protected:
	void* malloc(size_t size)
	{
		MemoryHeap* pHeap=(MemoryHeap*)::malloc(sizeof(HeapHeader)+size);
		if(pHeap){
			pHeap->header.size=size;
			return &pHeap->pBuffer;
		}
		return NULL;
	}
	bool free(void* pMem)
	{
		MemoryHeap* pHeap=(MemoryHeap*)((char*)pMem-sizeof(HeapHeader));
		if(pHeap->header.size>MAX_SIZE){
			::free(pHeap);
			return true;
		}
		return false;
	}
private:
	MemoryChunk** pChunkList;
	int chunkcount;
};
#endif
ObejctManager.h文件,用于实现对象的创建与管理,比较简易。
#ifndef OBJECT_MANAGER_H
#define OBJECT_MANAGER_H
#include "StaticMemory.h"
/** @class ObjectManager
 * 实现利用内存池创建对象
 * 要求对象具有缺省构造函数
 */
template<typename T>
class ObjectManager
{
public:
	typedef T ObjectType;

	static ObjectType* Create(StaticMemory* pool)
	{
		void* pobject=pool->Malloc(sizeof(T));
		new(pobject) ObjectType();
		return static_cast<ObjectType*>(pobject);
	}
	static void Delete(StaticMemory* pool, ObjectType* pobject)
	{
		pobject->~ObjectType();
		pool->Free(pobject);
	}
};
#endif
测试结果:

分单线程和多线程进行测试,重复的内存分配与释放在实际使用中是不太可能的,为了模拟实际使用,通过随机数来确定分配内存大小,同时也通过随机数来确定分配与释放操作。在测试过程中限制最大分配大小为1024,目的是为了测试小内存块的分配情况对比。

内存池单线程测试结果
分配与释放次数malloc/free内存池
                                                        100,000            0.01s        0.01s
                                                      1,000,000            0.15s        0.11s
                                                     10,000,000            1.26s        0.60s
                                                    100,000,000            9.21s        5.99s
                                                  1,000,000,000            92.70s        61.46s


内存池多线程测试结果
   线程数目                malloc/free                      内存池
1/1,000,000                  0.15s                      0.10s
2/1,000,000                 1.49s                      0.73s
4/1,000,000                 9.945s                      6.71s
8/1,000,000                 45.60s                     28.82s
进行多线程测试主要是测试多线程运行下,加锁给内存分配带来的影响,因此为了排除CPU的影响,测试采用的机器为16盒,16G内存的Linux服务器。

具体配置如下:

Intel(R) Xeon(R) CPU           E5630  @ 2.53GHz

stepping        : 2
cpu MHz         : 2527.084

cache size      : 12288 KB


微信扫码订阅
UP更新不错过~
关注
  • 13
    点赞
  • 25
    收藏 更改收藏夹
  • 打赏
    打赏
  • 18
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__cc__

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值