也许有人会问:内存池是什么呢? 主要用来干什么的?
大家都知道,C++可以使用指针来操作堆内存以获得高效率的内存访问,一般会使用全局new, delete的内存管理函数来申请和分配内存,但在大量使用内存分配的程序里----比如:网络游戏服务器的消息,每一条消息就分配一块内存,等消息处理完后,又要释放内存,这样来来回回的new, delete操作,效率会大大折扣,因为new,delete函数不可能预先知道你的对象要分配多大空间,每一个都要从当前的堆中寻找一块可用的空间来分配一个对象,最要命的是当你delete后,内存还要重新整理内存块,这样频繁操作将造成低效率结果。
于是,一个新的思想诞生了,如果当你满足以下条件时,你可以使用内存池来提高效率:
(1) 当你频繁地操作一类对象时,即你老是new 和 delete
(2) 当你知道使用一类对象的最大对象数时
你可以使用内存池,它是预先分配一定数量的一段内存空间,这一段空间预先分配了某类对象的数量,然后也可以像使用new, delete那样的方式来管理内存,不同在于这一段内存空间的管理使用者要调用内存池提供的相应函数(CreateObj, DestoryObj)来操作,而且堆内存由CObjectPool来管理。这样可以获得高效率的内存操作。
为什么这样子就可以高效率呢?恐怕你有点质疑,人家平常都用new, delete也挺爽的。好了,还是看下面内存池的设计思路吧!
一、我们为了适应不同的类对象,我们使用了模板的接口类----CObjectPool
二、MemPool类,内存池的核心类。负责内存管理, 主要使用的数据结构是链表结构, 结构为: Object|index, Object是某一类对象的数据, 而index是它在内存中的索引号, 这个很有用, 它是用来维持[已分配列表] 和 [空闲列表] 的地址计算。注:(它是浪费了4个字节的空间,但它的功劳大于它的罪过,你说用不用呢?)
接下来应该贴代码了吧,大家都关注这个实质性的东西了!
//============================================================================// FileName :ObjectPool.h
// CreateDate:2008-02-20
// Author :chenguihong
// Des :内存池
//-------------------------------------------------------------------------------------
#ifndef __OBJECT_POOL_H__
#define __OBJECT_POOL_H__
#include "MemPool.h"
template <class T>
class CObjectPool
{
public:
CObjectPool(int ObjectReserve = 1000):m_MemPool(sizeof(T),ObjectReserve){}
~CObjectPool()
{
m_MemPool.Release();
}
T* CreateObj()
{
return static_cast<T*>(m_MemPool.Alloc());
}
void DestoryObj(void*p)
{
m_MemPool.DeAlloc(p);
}
int Release()
{
return m_MemPool.Release();
}
public:
MemPool m_MemPool;
};
#endif
//-----------------------------------------------------------------------
//======================================================================
// FileName :MemPool.h
// CreateDate:2008-02-20
// Author :chenguihong
// Des :线程安全的内存池
//-------------------------------------------------------------------------------------
#ifndef __MEM_POOL_H__
#define __MEM_POOL_H__
#include <windows.h>
#include "Mutex.h"
class MemPool
{
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//一块内存.内存块的固定大小为 blockSize * blocks.
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
public:
MemPool(size_t block_size, size_t block_reserved);
bool Init(size_t block_size, size_t block_reserved);
int Release();
//----------------------------------------------------
//查看还有多少空间可以用
//----------------------------------------------------
size_t Capacity();
//----------------------------------------------------
//分配一个内存和释放一个内存
//----------------------------------------------------
void* Alloc();
void DeAlloc(void* p);
//----------------------------------------------------
//判断一个指针是不是由这个Pool分配的。
//----------------------------------------------------
bool Is_ptr(void* p);
private:
size_t m_blockSize; //每一个块的大小,即FixedAlloctor能分配的大小. 当
m_blockSize不能为1.
size_t m_blocks; //每个MemChunck中,Block的个数.
size_t m_avaliableBlocks; // 内存池中,空闲的块的个数
//内存块的地址
unsigned char* m_pData;
//第一个可用块,一个可用块的大小是由blockSize指定的.
unsigned long m_firstAvailableBlock;
unsigned long m_blocksAvailable; //是不是可用的空闲块
CMutex m_Mutex;
};
#endif
//---------------------------------------------------------------------------------------
/*
MemPool.cpp
*/
#include "MemPool.h"
#include <cassert>
#include <tchar.h>
#include <iostream>
using namespace std;
/********************************************************
内存池分配器.也就是一个固定大小的内存分配器.
Object|index 结构
*********************************************************/
MemPool::MemPool(size_t block_size, size_t block_reserved)
{
m_pData = NULL;
m_avaliableBlocks = 0;
Init(block_size,block_reserved);
}
bool MemPool::Init(size_t blockSize, size_t blocks)
{
assert(blockSize > 0);
assert(blocks > 0 && blocks < 10000000); //暂定10000000个,应该足够了吧!
m_pData = (unsigned char*) malloc((blockSize + sizeof(unsigned long)) * blocks);
if(m_pData == NULL)
return false;
m_firstAvailableBlock = 0;
m_blocks = blocks;
m_blocksAvailable = blocks;
m_blockSize = blockSize;
//填充内存块的链
unsigned long i = 0;
unsigned char* p = m_pData;
unsigned long* pNext = NULL;
for (; i != blocks; p += (blockSize + sizeof(unsigned long)))
{
pNext = (unsigned long*) (p + blockSize); //200000个整型溢出啊,加大马力
*pNext = ++i;
}
return true;
}
int MemPool::Release()
{
//请确保调用者释放指针方能执行操作
if(m_blocksAvailable < m_blocks) //出错说明有内存申请了但还没有被释放
{
int Result = m_blocks - m_blocksAvailable;
return Result;
}
if(m_pData != NULL)
{
free((unsigned char*)m_pData);
m_pData = NULL;
return 0;
}
}
void* MemPool::Alloc()
{
//多线程访问要加锁, 也可以用守卫来自动完成(线程安全)
Mutex_Guard sc(&m_Mutex);
unsigned char* pResult = NULL;
if(m_blocksAvailable > 0)
{
//把第一个可用返回给用户
//cout<<"申请到第"<<m_firstAvailableBlock+1<<"块"<<endl;
pResult = m_pData + (m_firstAvailableBlock * (m_blockSize + sizeof(unsigned
long)));
m_firstAvailableBlock = *(unsigned long*)(pResult + m_blockSize);
memset(pResult, 0, m_blockSize); //清0
m_blocksAvailable--;
}
else
{
return pResult;
}
return pResult;
}
void MemPool::DeAlloc(void* p)
{
//多线程访问要加锁, 用守卫来自动完成(线程安全)
Mutex_Guard sc(&m_Mutex);
assert(Is_ptr(p)); //检查是不是非法指针
unsigned char* toRelease = static_cast<unsigned char*>(p);
//把释放掉的块加入到表头里.新建一个表头,表头下一个块指向原来的第一个可用块
* ((unsigned long*)(toRelease + m_blockSize)) = m_firstAvailableBlock;
//第一个可用块指向表头
m_firstAvailableBlock = (toRelease - m_pData) / (m_blockSize + sizeof(unsigned
long));
//块对齐检查
assert(m_firstAvailableBlock == (toRelease - m_pData) / (m_blockSize + sizeof
(unsigned long)));
m_blocksAvailable++;
//cout<<"释放掉第"<<m_firstAvailableBlock+1<<"块"<<endl;
}
bool MemPool::Is_ptr(void* p)
{
//内存不在这个里面。也不是他分配的。
if(p < m_pData || p > (m_pData + (m_blockSize + sizeof(unsigned long)) * (m_blocks-1)))
return false;
//指针没在blockSize边界上对齐.肯定不是由这个MemPool分配的
long result = ((unsigned char*)p - m_pData) % (m_blockSize + sizeof(unsigned long));
if(result != 0)
return false;
return true;
}
//----------------------------------------------------
//查看还有多少空间可以用
//----------------------------------------------------
size_t MemPool::Capacity()
{
return m_blocksAvailable;
}
使用方法: CObjectPool<int> m_ObjectPool(10000); //10000个对象
int *p = m_ObjectPool.CreateObj(); //相当于new
m_ObjectPool.DestoryObj(p); //相当于delete
好了,代码到些结束,该出测试报告了吧!
性能测试, 当当当当.....
与普通的系统函数new和delete作对比, 各自开10条线程进行内存申请1,000,000对象, 每条线程反复地进
行申请和释放。
在CPU:Pentium D 3.0G, 内存1G的情况下:
结果: (1) 采用内存池用时: 5328ms
(2) 采用new 和 delete用时:27078ms
如果程序中有什么BUG或有什么看法的话,欢迎留言/评论,谢谢!