也許有人會問:內存池是什么呢? 主要用來干什么的?
大家都知道,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或有什么看法的話,歡迎留言/評論,謝謝!