一個記憶體池C++類的實作
在程式設計領域,程式員經常需要及時動态地産生出一些小型對象,例如讀取解析檔案時臨時需要的緩沖區,動态建立視圖時需要的視圖對象,遊戲程式中怪物,特效,場景物乃至于低級的連結清單節點等等。如果程式員隻是天真地使用new和delete,在經過程式執行中不斷反複的配置設定釋放記憶體後,很快就會使原本完整連續的可用記憶體塊變得支離破碎,這就是記憶體碎片即Memory Fragmentation問題。更糟的是記憶體碎片問題往往很難檢測出來。
為了盡可能降低記憶體碎片的狀況,并且得到高效的記憶體管理程式,除了從增加計算機配置,如增加記憶體條,和從程式的邏輯上着手,如減少讀取解析檔案時臨時需要的緩沖區,減少動态建立視圖的次數,減少遊戲程式中怪物,特效,場景物出現頻度之外,同時還需要從程式的低級層面,即從記憶體配置設定的功能面着手改善。
在使用者每次new操作從系統索取記憶體空間時,C++的函數庫除了配置所要求的記憶體塊大小以外,還會另外生成一小塊額外的記憶體塊以記載相關的資料。對于經常進行産生與銷毀的小型對象來說,這樣的行為就顯得十分浪費而不具效率。如果能夠一次性的配置設定出一大塊記憶體,然後再根據使用者的需求傳回部分記憶體塊,這樣就能改善new操作所産生的額外負擔。
在這裡,我參考早年微軟MSJ期刊的一些資料寫出了一個記憶體池C++類:CMemPool,這個記憶體池建立在樹狀的雙向連結清單之上的,其相關結構體定義如下:
//樹型記憶體塊配置設定結構.
typedef struct tagMEMORY_BLOCK MEMORY_BLOCK, *LPMEMORY_BLOCK;
struct tagMEMORY_BLOCK
{
DWORD dwIndex;
DWORD dwSize;
LPMEMORY_BLOCK lpNext;
LPMEMORY_BLOCK lpPrev;
LPBYTE lpMemory;
};
//記憶體池标頭結構
typedef struct tagHEADER HEADER, *LPHEADER;
struct tagHEADER
{
LPMEMORY_BLOCK lpHead;
HANDLE hHeap;
};
好了,我就不再啰嗦了,下面列出CMemPool類的源碼,有不對之處,請不吝賜教!~~~
CMemPool類的頭檔案MemPool.h代碼如下:
//====================================================================
// 記憶體池類CMemPool的頭檔案CMemPool.h
//by 一劍(Loomman),QQ:28077188,MSN: [email protected] QQ裙:30515563
//====================================================================
#ifndef MEMPOOL_H
#define MEMPOOL_H
#define STRICT
#define LEAN_AND_MEAN
#include <windows.h>
#include <assert.h>
#include <stdio.h>
#include <mbstring.h>
//預設記憶體配置設定頁大小設為8KB
#define MEMPOOL_BLOCK_SIZE 8192
class CMemPool
{
public:
CMemPool();
~CMemPool();
BOOL Initialize();
VOID Destroy();
LPVOID GetAlignedMemory(DWORD dwSize, DWORD dwAlignSize);
LPSTR GetDuplicateStringA(LPCSTR szSrc);
LPWSTR GetDuplicateStringW(LPCWSTR szSrc);
inline LPVOID GetMemory(DWORD dwSize)
{
return GetAlignedMemory(dwSize, 0);
}
inline TCHAR* GetStringBuffer(DWORD dwLen)
{
return (TCHAR*)GetAlignedMemory(dwLen * sizeof(TCHAR), 0);
}
inline LPSTR GetStringBufferA(DWORD dwLen)
{
return (LPSTR)GetAlignedMemory(dwLen * sizeof(CHAR), sizeof(CHAR));
}
inline LPWSTR GetStringBufferW(DWORD dwLen)
{
return (LPWSTR)GetAlignedMemory(dwLen * sizeof(WCHAR), sizeof(WCHAR));
}
inline DWORD* GetDwordBuffer()
{
return (DWORD*)GetAlignedMemory(sizeof(DWORD), 0);
}
private:
BOOL AddMemory(DWORD dwSize);
LPVOID handle;
};
#endif //MEMPOOL_H
CMemPool類的實作檔案MemPool.cpp代碼如下:
//====================================================================
// 記憶體池類CMemPool的實作檔案CMemPool.cpp
//by 一劍(Loomman),QQ:28077188,MSN: [email protected] QQ裙:30515563
//====================================================================
#include "MemPool.h"
//樹型記憶體塊配置設定結構.
typedef struct tagMEMORY_BLOCK MEMORY_BLOCK, *LPMEMORY_BLOCK;
struct tagMEMORY_BLOCK
{
DWORD dwIndex;
DWORD dwSize;
LPMEMORY_BLOCK lpNext;
LPMEMORY_BLOCK lpPrev;
LPBYTE lpMemory;
};
//記憶體池标頭結構
typedef struct tagHEADER HEADER, *LPHEADER;
struct tagHEADER
{
LPMEMORY_BLOCK lpHead;
HANDLE hHeap;
};
//記憶體池對象構造函數
CMemPool::CMemPool()
{
handle = NULL;
}
//記憶體池對象析構函數
CMemPool::~CMemPool()
{
Destroy();
}
//記憶體池對象初始化,首次配置設定8KB記憶體作為記憶體池
BOOL CMemPool::Initialize()
{
if( NULL == handle )
{
HANDLE procHeap = GetProcessHeap();
// 配置設定記憶體池的頭節點.
handle = HeapAlloc(procHeap, 0, sizeof(HEADER));
if (handle)
{
LPHEADER header = (LPHEADER)handle;
// 配置設定頭節點成功,現在初始化記憶體池.
header->lpHead = NULL;
header->hHeap = procHeap;
//初次實際配置設定8KB記憶體到記憶體池.
BOOL ableToAddMemory = AddMemory(0);
if (!ableToAddMemory)
{
//配置設定記憶體失敗,系統資源瓶頸!
HeapFree(header->hHeap, 0, handle);
handle = NULL;
}
}
}
return (handle != NULL);
}
VOID CMemPool::Destroy()
{
if(handle != NULL)
{
LPMEMORY_BLOCK nextBlock;
LPMEMORY_BLOCK blockToFree;
LPHEADER poolHeader = (LPHEADER)handle;
//周遊連結清單,進行釋放記憶體操作.
blockToFree = poolHeader->lpHead;
while (blockToFree != NULL)
{
nextBlock = blockToFree->lpNext;
HeapFree(poolHeader->hHeap, 0, blockToFree);
blockToFree = nextBlock;
}
//别忘記了,記憶體池頭結點也需要釋放.
HeapFree(poolHeader->hHeap, 0, handle);
handle = NULL;
}
}
LPVOID CMemPool::GetAlignedMemory(DWORD dwSize, DWORD dwAlignSize)
{
assert(handle != NULL);
BOOL haveEnoughMemory = TRUE;
LPVOID lpMemory = NULL;
LPHEADER poolHeader = (LPHEADER)handle;
LPMEMORY_BLOCK currentBlock;
DWORD sizeNeeded;
DWORD padLength;
currentBlock = poolHeader->lpHead;
// 判斷是否需要更多的記憶體,如果是,則配置設定之.
sizeNeeded = dwSize;
if (currentBlock->dwSize - currentBlock->dwIndex < sizeNeeded + dwAlignSize)
{
haveEnoughMemory = AddMemory(sizeNeeded + dwAlignSize);
currentBlock = poolHeader->lpHead;
}
// 現在有了足夠的記憶體,傳回它!
if (haveEnoughMemory)
{
if (dwAlignSize)
{
padLength = (DWORD)currentBlock + sizeof(MEMORY_BLOCK) + currentBlock->dwIndex;
currentBlock->dwIndex += (dwAlignSize - (padLength % dwAlignSize)) % dwAlignSize;
}
//這裡得到了記憶體位址,傳回它!
lpMemory = (LPVOID)&(currentBlock->lpMemory[currentBlock->dwIndex]);
currentBlock->dwIndex += sizeNeeded;
}
return lpMemory;
}
LPSTR CMemPool::GetDuplicateStringA(LPCSTR szSrc)
{
assert(szSrc);
DWORD dwBytes = (_mbslen((const unsigned char*)szSrc) + 1) * sizeof(CHAR);
LPSTR pString = (LPSTR)GetAlignedMemory(dwBytes, sizeof(CHAR));
if (pString)
{
_mbscpy_s((unsigned char*)pString, dwBytes, (const unsigned char*)szSrc);
}
return pString;
}
LPWSTR CMemPool::GetDuplicateStringW(LPCWSTR szSrc)
{
assert(szSrc);
DWORD dwBytes = (wcslen(szSrc) + 1) * sizeof(WCHAR);
LPWSTR pString = (LPWSTR)GetAlignedMemory(dwBytes, sizeof(WCHAR));
if (pString)
{
wcscpy_s(pString, dwBytes, szSrc);
}
return pString;
}
BOOL CMemPool::AddMemory(DWORD dwSize)
{
LPBYTE allocedMemory;
LPMEMORY_BLOCK newBlock;
LPHEADER poolHeader = (LPHEADER)handle;
DWORD sizeNeeded;
assert(poolHeader != NULL);
// 計算需要配置設定記憶體的最小數量,并試圖配置設定之.
if (dwSize + sizeof(MEMORY_BLOCK) > MEMPOOL_BLOCK_SIZE)
{
sizeNeeded = dwSize + sizeof(MEMORY_BLOCK);
}
else
{
sizeNeeded = MEMPOOL_BLOCK_SIZE;
}
allocedMemory = (LPBYTE)HeapAlloc(poolHeader->hHeap, 0, sizeNeeded);
if (allocedMemory)
{
// 使記憶體塊的頭部存儲一個MEMORY_BLOCK結構.
newBlock = (LPMEMORY_BLOCK)allocedMemory;
newBlock->dwSize = sizeNeeded - sizeof(MEMORY_BLOCK);
newBlock->lpMemory = allocedMemory + sizeof(MEMORY_BLOCK);
newBlock->dwIndex = 0;
// 把記憶體塊連結到list中.
if(poolHeader->lpHead)
{
poolHeader->lpHead->lpPrev = newBlock;
}
newBlock->lpNext = poolHeader->lpHead;
newBlock->lpPrev = NULL;
poolHeader->lpHead = newBlock;
}
// 如果allocedMemory 不是 NULL, 則表明我們成功了.
return allocedMemory != NULL;
}
CMemPool類使用示範程式代碼如下:
#include <TCHAR.H>
#include "MemPool.h"
int main()
{
CMemPool mp;
assert( mp.Initialize() );
for(int i = 0; i<100; i++)
{
TCHAR* psz = mp.GetStringBuffer(8192);
_stprintf_s(psz, 8192, TEXT("now i=%d/n"), i);
_tprintf(psz);
}
mp.Destroy();
return getchar();
}
在其中的_tprintf(psz);和mp.Destroy();這兩行打上斷點,調試運作,觀察windows任務管理器關于該程序的記憶體使用量呈明顯的規律性增長。最後當執行完mp.Destroy();後該程序的記憶體使用量又恢複到未使用記憶體池時的狀态。by Loomman, QQ:28077188, MSN: [email protected] QQ裙:30515563 ☆程式天堂☆ 請尊重作者原創,轉載注明來自裂帛一劍部落格,謝謝合作。