使用C/C++實現內存池技術
內存管理技術是開發多媒體應用和服務的很重要的知識。DMSP應用中會有頻繁的緩衝區的創建和釋放操作,這些操作會降低程序的運行效率和運行時間。本節在講解內存池技術的同時,講解對象的創建和使用方法。
內存池技術主要的思想是:被創建的緩衝區,在使用完後,並不立即釋放,而是存放在一個空閒隊列池中;當程序需要新的緩衝區時,首先從內存池中獲取可用的緩衝區;在內存池中沒有可用的緩衝區時,纔去創建新的。通過這種內存池方法,可以減少頻繁的緩衝區的創建和釋放操作,減少內存碎片的發生,從而較有效的管理緩衝區。
內存池技術中,每一塊內存被當作一個內存對象(IXBufferItem),這些對象可以管理自已的生成周期;同時,結合內存管理器對象(IXBufferManager)的管理功能,在內存對象需要被釋放時,調用內存管理對象的相應接口(Reuse)通知管理對象自已需要被釋放,訊問是否被再次利用,並根據返回的情況決定是釋放自已,還是其他操作。
內存對象及其內存池管理對象被定義在下表中的源代碼文件中。x_buffer_manager.h定義了內存對象和內存池管理對象,還定義了相關的操作函數組。
源文件x_buffer_manager.h:
// x_buffer_manager.h: 緩衝區結點及其管理定義;
#ifndef _x_buffer_manager_h_
#define _x_buffer_manager_h_
#include "../include/x_nb_baseobject.h"
//緩衝區對象;
class IXBufferItem : public ISelfBaseObject
{
public:
virtual unsigned long __stdcall GetLength(unsigned long* pdwLength) = 0;
virtual unsigned long __stdcall SetLength(unsigned long dwLength) = 0;
virtual unsigned long __stdcall GetMaxLength(unsigned long* pdwLength) = 0;
virtual unsigned long __stdcall GetBuffer(unsigned char** ppdwBuffer) = 0;
virtual unsigned long __stdcall GetBufferAndLength(unsigned char** ppdwBuffer, unsigned long *pdwLength) = 0;
};
//緩衝區管理對象;
class IXBufferManage : public ISelfBaseObject
{
public:
virtual unsigned long __stdcall Init(long inMaxCount) = 0;
virtual unsigned long __stdcall Uninit(void) = 0;
virtual unsigned long __stdcall GetEmptySample(unsigned long inRequiredSize, IXBufferItem** outBuffer) = 0;
virtual unsigned long __stdcall Reuse(IXBufferItem* inSample) = 0;
};
#ifdef __cplusplus
extern "C" {
#endif
// CreateBufferItem: 創建緩衝區對象;
HRESULT __stdcall CreateBufferItem(LPVOID* lppvObj);
// CreateBufferManager: 創建緩衝區管理對象;
HRESULT __stdcall CreateBufferManager(LPVOID* lppvObj);
#ifdef __cplusplus
}
#endif
#endif //(!_x_buffer_manager_h_)
IXBufferItem和IXBufferManage都從ISelfBaseObject派生而來。還應注意上例源代碼中,使用虛函數和虛基類定義對象及其接口的方法。在實現這兩個對象時,需要創建他們的實例;這兩個函數的實例分別定義在下列源代碼文件中。
源代碼文件:buffmgr.h
// buffmgr: 緩衝區對象及其管理接口組;
#ifndef _buffmgr_h_
#define _buffmgr_h_
#include "../include/x_buffer_manager.h"
……
#endif //(!_buffmgr_h_)
源代碼文件:buffer_item.h
// buffer_item: 緩衝區對象接口組;
#ifndef _buffer_item_h_
#define _buffer_item_h_
#include "buffmgr.h"
class CXBufferManage;
// CXBufferItem: 緩衝區對象接口組;
class CXBufferItem : public IXBufferItem
{
public: //ISelfBaseObject接口;
virtual HRESULT __stdcall QueryInterface(REFIID iid,
LPVOID *lppvObject);
virtual ULONG __stdcall AddRef(VOID);
virtual ULONG __stdcall Release(VOID);
virtual DWORD __stdcall GetVersion(LPDWORD pdwHigh);
public: //IXBufferItem接口;
virtual unsigned long __stdcall GetLength(unsigned long* pdwLength);
virtual unsigned long __stdcall SetLength(unsigned long dwLength);
virtual unsigned long __stdcall GetMaxLength(unsigned long* pdwLength);
virtual unsigned long __stdcall GetBuffer(unsigned char** ppdwBuffer);
virtual unsigned long __stdcall GetBufferAndLength(unsigned char** ppdwBuffer, unsigned long *pdwLength);
public:
CXBufferItem(unsigned long inSize, CXBufferManage* pSmpMgr);
CXBufferItem();
~CXBufferItem();
public:
unsigned long SetBufferManage(CXBufferManage* pSmpMgr);
unsigned long VerifyBufferSize(unsigned long inRequiredSize);
public:
CXBufferItem* m_pNext; //後一個對象;
private:
long m_cRef; //引用的計數;
unsigned char* m_pBuffer; //緩衝區地址;
unsigned long m_dwBufSize; //緩衝區長度;
unsigned long m_dwDataSize; //數據的長度;
CXBufferManage* m_pSmpAdmin; //緩衝的管理;
};
#endif //(!_buffer_item_h_)
源代碼文件:buffer_manage.h
// buffer_manage.h: 緩衝區對象管理接口組;
#ifndef _buffer_manage_h_
#define _buffer_manage_h_
#include "buffmgr.h"
class CXBufferItem;
// CXBufferManage: 緩衝區對象管理接口組;
class CXBufferManage : public IXBufferManage
{
public: //ISelfBaseObject接口;
virtual HRESULT __stdcall QueryInterface(REFIID iid,
LPVOID *lppvObject);
virtual ULONG __stdcall AddRef(VOID);
virtual ULONG __stdcall Release(VOID);
virtual DWORD __stdcall GetVersion(LPDWORD pdwHigh);
public: //IXBufferManage接口;
virtual unsigned long __stdcall Init(long inMaxCount);
virtual unsigned long __stdcall Uninit(void);
virtual unsigned long __stdcall GetEmptySample(unsigned long inRequiredSize, IXBufferItem** outBuffer);
virtual unsigned long __stdcall Reuse(IXBufferItem* inSample);
public:
CXBufferManage();
~CXBufferManage();
private:
long m_cRef; //引用計數;
CXBufferItem* m_pIdleHead; //空閒隊列頭;
CXBufferItem* m_pIdleTail; //空閒隊列尾;
long m_nMaxCount; //最大個數;
long m_nCurCount; //當前個數;
……
};
……
#endif //(!_buffer_manage_h_)
類CXBufferItem實現了IXBufferItem的所有的虛擬函數接口,同時在其對象內部保存了一個緩衝區對象。其中m_pBuffer指向緩衝區的首地址,m_dwBufSize是緩衝區的長度,m_dwDataSize是緩衝區中的數據長度;同時,m_pSmpAdmin是內存對象的管理器,當內存對象需要釋放時,首先檢查該對象是否存在,如果存在,則調用該對象的Reuse接口通知它回收回已;如果管理器對象不存在或者不回收自己,則CXBufferItem釋放直接釋放自己,這通過它的接口Release來實現。該類還有一個成員是m_pNext,它被內存池管理器對象使用。類CXBufferItem的其它對象主要是便於緩衝區的管理。具體的實現見下例中的源代碼。
源代碼文件:buffer_item.cpp
// buffer_item: 緩衝區結點函數組;
#include "buffer_item.h"
#include "buffer_manage.h"
// ISelfBaseObject接口函數組;
HRESULT __stdcall CXBufferItem::QueryInterface(REFIID iid,
LPVOID *lppvObject)
{
*lppvObject = reinterpret_cast<ISelfBaseObject*>(this);
this->AddRef();
return S_OK;
}
ULONG __stdcall CXBufferItem::AddRef(VOID)
{
return ::InterlockedIncrement(&m_cRef);
}
ULONG __stdcall CXBufferItem::Release(VOID)
{
if(0 == ::InterlockedDecrement(&m_cRef))
{
if(m_pSmpAdmin != NULL)
{
//調用管理器回收緩衝區;
m_pSmpAdmin->Reuse(this);
}
else {
//釋放緩衝區;
delete this;
}
return 0;
}
return (ULONG)m_cRef;
}
DWORD __stdcall CXBufferItem::GetVersion(LPDWORD pdwHigh)
{
if(pdwHigh != NULL)
{
*pdwHigh = 0x00000001;
}
return 0x00010000;
}
//構造函數(傳入尺寸和管理對象);
CXBufferItem::CXBufferItem(unsigned long inSize,
CXBufferManage* pSmpMgr)
{
m_cRef = 0;
m_dwBufSize = 0;
m_dwDataSize = 0;
m_pBuffer = new unsigned char[inSize];
if(m_pBuffer != NULL)
{
m_dwBufSize = inSize;
m_dwDataSize = inSize;
}
m_pSmpAdmin = pSmpMgr;
if(m_pSmpAdmin != NULL)
{
m_pSmpAdmin->AddRef();
}
m_pNext = NULL;
}
//缺省構造函數;
CXBufferItem::CXBufferItem()
{
m_pBuffer = NULL;
m_dwBufSize = 0;
m_dwDataSize = 0;
m_pSmpAdmin = NULL;
m_pNext = NULL;
}
//析構函數;
CXBufferItem::~CXBufferItem()
{
//釋放緩衝區;
if(m_pBuffer != NULL)
{
delete[] m_pBuffer;
}
m_pBuffer = NULL;
//釋放對管理對象的引用;
if(m_pSmpAdmin != NULL)
{
m_pSmpAdmin->Release();
}
m_pSmpAdmin = NULL;
}
//設置管理對象;
unsigned long CXBufferItem::SetBufferManage(CXBufferManage* pSmpMgr)
{
//釋放原有的管理對象(如果有);
if(m_pSmpAdmin != NULL)
{
m_pSmpAdmin->Release();
}
//保存管理對象(增加引用計數);
m_pSmpAdmin = pSmpMgr;
if(m_pSmpAdmin != NULL)
{
m_pSmpAdmin->AddRef();
}
return 0;
}
//調用緩衝區的大小(在原緩衝區較小時);
unsigned long CXBufferItem::VerifyBufferSize(unsigned long inRequiredSize)
{
unsigned long hr = 0;
if(inRequiredSize > m_dwBufSize)
{
if(m_pBuffer != NULL)
{
delete[] m_pBuffer;
m_pBuffer = NULL;
}
m_pBuffer = new unsigned char[inRequiredSize];
if(m_pBuffer != NULL)
{
m_dwBufSize = inRequiredSize;
m_dwDataSize = inRequiredSize;
hr = 0;
}
else {
m_dwBufSize = NULL;
m_dwDataSize = NULL;
hr = -1;
}
}
return hr;
}
//獲取緩衝區中數據的長度;
unsigned long __stdcall CXBufferItem::GetLength(unsigned long *pdwLength)
{
*pdwLength = m_dwDataSize;
return 0;
}
//設置緩衝區中數據的長度;
unsigned long __stdcall CXBufferItem::SetLength(unsigned long dwLength)
{
m_dwDataSize = dwLength;
return 0;
}
//獲取緩衝區的總長度;
unsigned long __stdcall CXBufferItem::GetMaxLength(unsigned long *pdwLength)
{
*pdwLength = m_dwBufSize;
return 0;
}
//獲取緩衝區的地址;
unsigned long __stdcall CXBufferItem::GetBuffer(unsigned char **ppdwBuffer)
{
*ppdwBuffer = m_pBuffer;
return 0;
}
//獲取緩衝區的地址和數據的長度;
unsigned long __stdcall CXBufferItem::GetBufferAndLength(unsigned char **ppdwBuffer, unsigned long *pdwLength)
{
*ppdwBuffer = m_pBuffer;
*pdwLength = m_dwDataSize;
return 0;
}
類CXBufferManage實現了IXBufferManage的所有的虛擬函數接口,同時在其對象內部設置一個單向鏈表用於存儲空閒內存對象。m_pIdleHead指向該表的表頭;m_pIdleTail指向該表的表尾;m_nMaxCount設置最大可以保存的空閒對象的個數;m_nCurCount保存當前空閒對象的個數。類CXBufferManage的接口Init用於設置最大可以保存的空閒對象的個數;在該類CXBufferManage的實例在釋放時,會調用Uninit釋放所有的被保存的空閒內存對象。
調用者調用該類CXBufferManage的GetEmptySample接口獲取內存對象;GetEmptySample接口首先從空閒隊列中獲取內存對象;如果沒有可用的空閒對象,則創建新的內存對象;當內存對象IXBufferItem不再使用時,調用CXBufferManage的Reuse接口,該接口首先檢查空閒內存對象是否達到最大值,如果沒有,則把傳入的對象加入到空閒隊列中;如果達到最大值,則不加入到隊列中。類CXBufferManage的實現見下例中的源代碼。
源代碼文件:buffer_manage.cpp
……
// buffer_manage.cpp: 緩衝區對象管理接口組;
#include "buffer_item.h"
#include "buffer_manage.h"
// ISelfBaseObject接口函數組;
HRESULT __stdcall CXBufferManage::QueryInterface(REFIID iid,
LPVOID *lppvObject)
{
*lppvObject = reinterpret_cast<ISelfBaseObject*>(this);
this->AddRef();
return S_OK;
}
ULONG __stdcall CXBufferManage::AddRef(VOID)
{
return ::InterlockedIncrement(&m_cRef);
}
ULONG __stdcall CXBufferManage::Release(VOID)
{
if(0 == ::InterlockedDecrement(&m_cRef))
{
delete this;
return 0;
}
return (ULONG)m_cRef;
}
DWORD __stdcall CXBufferManage::GetVersion(LPDWORD pdwHigh)
{
if(pdwHigh != NULL)
{
*pdwHigh = 0x00000001;
}
return 0x00010000;
}
//構造函數;
CXBufferManage::CXBufferManage()
{
m_cRef = 1;
m_nMaxCount = 10;
m_nCurCount = 0;
m_pIdleHead = NULL;
m_pIdleTail = NULL;
}
//析構函數(釋放所有管理的對象);
CXBufferManage::~CXBufferManage()
{
Uninit();
}
//初始化管理器(指明最大個數);
unsigned long __stdcall CXBufferManage::Init(long inMaxCount)
{
if(inMaxCount > m_nMaxCount)
{
m_nMaxCount = inMaxCount;
}
return 1;
}
//釋放管理器的信息;
unsigned long __stdcall CXBufferManage::Uninit(void)
{
CXBufferItem* pSample = NULL;
CXBufferItem* pTmp = NULL;
//釋放管理的所有緩衝區對象;
pSample = m_pIdleHead;
while(pSample)
{
pTmp = pSample->m_pNext;
pSample->SetBufferManage(NULL);
pSample->Release();
pSample = pTmp;
}
m_pIdleHead = NULL;
m_pIdleTail = NULL;
m_nCurCount = 0;
return 1;
}
//創建或獲取緩衝區對象;
unsigned long __stdcall CXBufferManage::GetEmptySample(unsigned long inRequiredSize, IXBufferItem** outBuffer)
{
CXBufferItem* pSample = NULL;
//檢查參數;
if(!outBuffer) return 0;
*outBuffer = NULL;
//創建新的緩衝區對象(如果沒有);
if(m_pIdleHead == NULL)
{
//創建新對象;
pSample = new CXBufferItem(inRequiredSize, this);
if(pSample == NULL)
{
return 0;
}
pSample->VerifyBufferSize(inRequiredSize);
}
else {
pSample = m_pIdleHead;
pSample->AddRef();
//調整表;
m_pIdleHead = m_pIdleHead->m_pNext;
if(m_pIdleHead == NULL)
{
m_pIdleTail = NULL;
}
m_nCurCount --;
}
//調整結點信息;
pSample->VerifyBufferSize(inRequiredSize);
*outBuffer = (IXBufferItem*)pSample;
return 1;
}
//回收緩衝區對象(直接加入到空閒表中);
unsigned long __stdcall CXBufferManage::Reuse(IXBufferItem* inSample)
{
CXBufferItem* pSam = NULL;
//檢查參數;
if(!inSample) return 0;
pSam = (CXBufferItem*)inSample;
pSam->m_pNext = NULL;
//結點嘗試加入到表的尾部;
if(m_nCurCount < m_nMaxCount)
{
if(!m_pIdleTail)
{
m_pIdleHead = pSam;
m_pIdleTail = pSam;
}
else {
m_pIdleTail->m_pNext = pSam;
m_pIdleTail = pSam;
}
m_nCurCount ++;
return 1;
}
return 0;
}
對象IXBufferItem和IXBufferManage可以保存在DLL動態庫文件中,本例通過輸出函數接口CreateBufferItem和CreateBufferManager來創建這兩個對象;同時,通過相應的DEF文件來設置輸出的接口。實現細節見下表中的源代碼。
源代碼文件:buffmgr.cpp
// buffmgr: 緩衝區對象及其管理接口組;
#include "buffmgr.h"
#include "buffer_item.h"
#include "buffer_manage.h"
……
//全局變量;
HINSTANCE ghInstance = NULL; //庫的實例句柄;
//動態庫輸出主函數;
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
ghInstance = (HINSTANCE)hModule;
DisableThreadLibraryCalls(ghInstance);
break;
}
case DLL_PROCESS_DETACH:
{
break;
}
}
return TRUE;
}
// CreateBufferItem: 創建緩衝區對象;
HRESULT __stdcall CreateBufferItem(LPVOID* lppvObj)
{
CXBufferItem* lpirc = NULL;
HRESULT hr = S_OK;
//創建一個對象;
lpirc = new CXBufferItem;
if(lpirc == NULL)
{
return E_OUTOFMEMORY;
}
//查尋對象以獲取接口;
hr = lpirc->QueryInterface(IID_ISelfBaseObject, lppvObj);
lpirc->Release();
//返回處理結果;
return hr;
}
// CreateBufferManager: 創建緩衝區管理對象;
HRESULT __stdcall CreateBufferManager(LPVOID* lppvObj)
{
CXBufferManage* lpirc = NULL;
HRESULT hr = S_OK;
//創建一個對象;
lpirc = new CXBufferManage;
if(lpirc == NULL)
{
return E_OUTOFMEMORY;
}
//查尋對象以獲取接口;
hr = lpirc->QueryInterface(IID_ISelfBaseObject, lppvObj);
lpirc->Release();
//返回處理結果;
return hr;
}
……
源代碼文件:buffmgr.def
……
EXPORTS
CreateBufferItem @1
CreateBufferManager @2
……
對象IXBufferItem和IXBufferManage保存在DLL動態庫文件(buffmgr.dll)中,其關聯文件存放在buffmgr.lib文件中。使用該對象庫時,可以使用顯示調用方法,也可以使用隱式調用方法。示例文件buffer_reuse.cpp給出了一種隱式調用的方法。細節見相應的源代碼文件。
源代碼文件:buffer_resuse.cpp
// buffer_reuse: 重用緩衝區測試示例;
#include <stdio.h>
#include "../../include/x_buffer_manager.h"
#pragma comment(lib, "../../lib/buffmgr.lib")
int main(int argc, char* argv[])
{
IXBufferManage* pXBM = NULL;
IXBufferItem* pXBItem = NULL;
unsigned long nBufferSize = 64*1024;
long num = 0;
//創建緩衝管理器;
CreateBufferManager((void**)&pXBM);
if(pXBM == NULL)
{
printf("Error buffer manager!\r\n");
return -1;
}
//初始化緩衝池;
pXBM->Init(100);
//循環獲取和釋放緩衝區;
for(num=0; num<20; num++)
{
pXBM->GetEmptySample(nBufferSize, &pXBItem);
if(pXBItem != NULL)
{
//回收緩衝區;
pXBItem->Release();
pXBItem = NULL;
}
}
//釋放緩衝池;
pXBM->Uninit();
//刪除緩衝管理器;
pXBM->Release();
pXBM = NULL;
return 0;
}