天天看點

一個記憶體池C++類的實作

一個記憶體池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 ☆程式天堂☆ 請尊重作者原創,轉載注明來自裂帛一劍部落格,謝謝合作。

繼續閱讀