一个内存池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: Loomman@hotmail.com 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: Loomman@hotmail.com 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();后该进程的内存使用量又恢复到未使用内存池时的状态