使用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;
}