一个内存池C++类的实现

来源:互联网 发布:pl sql developer教程 编辑:程序博客网 时间:2024/05/18 12:01

一个内存池C++类的实现

在程序设计领域,程序员经常需要及时动态地产生出一些小型对象,例如读取解析文件时临时需要的缓冲区,动态创建视图时需要的视图对象,游戏程序中怪物,特效,场景物乃至于低级的链表节点等等。如果程序员只是天真地使用new和delete,在经过程序执行中不断反复的分配释放内存后,很快就会使原本完整连续的可用内存块变得支离破碎,这就是内存碎片即Memory Fragmentation问题。更糟的是内存碎片问题往往很难检测出来。

为了尽可能降低内存碎片的状况,并且得到高效的内存管理程序,除了从增加计算机配置,如增加内存条,和从程序的逻辑上着手,如减少读取解析文件时临时需要的缓冲区,减少动态创建视图的次数,减少游戏程序中怪物,特效,场景物出现频度之外,同时还需要从程序的低级层面,即从内存分配的功能面着手改善。

在用户每次new操作从系统索取内存空间时,C++的函数库除了配置所要求的内存块大小以外,还会另外生成一小块额外的内存块以记载相关的资料。对于经常进行产生与销毁的小型对象来说,这样的行为就显得十分浪费而不具效率。如果能够一次性的分配出一大块内存,然后再根据用户的需求传回部分内存块,这样就能改善new操作所产生的额外负担。

在这里,我参考早年微软MSJ期刊的一些资料写出了一个内存池C++类:CMemPool,这个内存池建立在树状的双向链表之上的,其相关结构体定义如下:

 

view plaincopy to clipboardprint?
  1. //树型内存块分配结构.   
  2. typedef struct tagMEMORY_BLOCK  MEMORY_BLOCK, *LPMEMORY_BLOCK;  
  3. struct tagMEMORY_BLOCK  
  4. {  
  5.     DWORD                 dwIndex;  
  6.     DWORD                 dwSize;  
  7.     LPMEMORY_BLOCK        lpNext;  
  8.     LPMEMORY_BLOCK        lpPrev;  
  9.     LPBYTE                lpMemory;  
  10. };  
  11.   
  12. //内存池标头结构   
  13. typedef struct tagHEADER  HEADER, *LPHEADER;  
  14. struct tagHEADER  
  15. {  
  16.     LPMEMORY_BLOCK   lpHead;  
  17.     HANDLE           hHeap;  
  18. };  

 

好了,我就不再啰嗦了,下面列出CMemPool类的源码,有不对之处,请不吝赐教!~~~

CMemPool类的头文件MemPool.h代码如下:

 

view plaincopy to clipboardprint?
  1. //====================================================================  
  2. //               内存池类CMemPool的头文件CMemPool.h   
  3. //by 一剑(Loomman),QQ:28077188,MSN: Loomman@hotmail.com QQ裙:30515563   
  4. //====================================================================   
  5. #ifndef MEMPOOL_H   
  6. #define MEMPOOL_H   
  7.   
  8. #define STRICT   
  9. #define LEAN_AND_MEAN   
  10.   
  11. #include <windows.h>   
  12. #include <assert.h>   
  13. #include <stdio.h>   
  14. #include <mbstring.h>   
  15.   
  16. //默认内存分配页大小设为8KB   
  17. #define     MEMPOOL_BLOCK_SIZE      8192   
  18.   
  19. class CMemPool  
  20. {  
  21. public:  
  22.   
  23.     CMemPool();  
  24.     ~CMemPool();  
  25.   
  26.     BOOL   Initialize();  
  27.     VOID   Destroy();  
  28.   
  29.     LPVOID GetAlignedMemory(DWORD dwSize, DWORD dwAlignSize);  
  30.     LPSTR  GetDuplicateStringA(LPCSTR szSrc);  
  31.     LPWSTR GetDuplicateStringW(LPCWSTR szSrc);  
  32.   
  33.     inline LPVOID GetMemory(DWORD dwSize)   
  34.     {  
  35.         return GetAlignedMemory(dwSize, 0);  
  36.     }  
  37.   
  38.     inline TCHAR* GetStringBuffer(DWORD dwLen)  
  39.     {  
  40.         return (TCHAR*)GetAlignedMemory(dwLen * sizeof(TCHAR), 0);  
  41.     }  
  42.   
  43.     inline LPSTR GetStringBufferA(DWORD dwLen)   
  44.     {  
  45.         return (LPSTR)GetAlignedMemory(dwLen * sizeof(CHAR), sizeof(CHAR));  
  46.     }  
  47.   
  48.     inline LPWSTR GetStringBufferW(DWORD dwLen)   
  49.     {  
  50.         return (LPWSTR)GetAlignedMemory(dwLen * sizeof(WCHAR), sizeof(WCHAR));  
  51.     }  
  52.   
  53.     inline DWORD* GetDwordBuffer()  
  54.     {  
  55.         return (DWORD*)GetAlignedMemory(sizeof(DWORD), 0);  
  56.     }  
  57.   
  58. private:  
  59.     BOOL   AddMemory(DWORD dwSize);  
  60.     LPVOID handle;  
  61. };  
  62.   
  63. #endif //MEMPOOL_H  

 

CMemPool类的实现文件MemPool.cpp代码如下:

 

view plaincopy to clipboardprint?
  1. //====================================================================   
  2. //               内存池类CMemPool的实现文件CMemPool.cpp   
  3. //by 一剑(Loomman),QQ:28077188,MSN: Loomman@hotmail.com QQ裙:30515563   
  4. //====================================================================   
  5. #include "MemPool.h"   
  6.   
  7. //树型内存块分配结构.   
  8. typedef struct tagMEMORY_BLOCK  MEMORY_BLOCK, *LPMEMORY_BLOCK;  
  9. struct tagMEMORY_BLOCK  
  10. {  
  11.     DWORD                 dwIndex;  
  12.     DWORD                 dwSize;  
  13.     LPMEMORY_BLOCK        lpNext;  
  14.     LPMEMORY_BLOCK        lpPrev;  
  15.     LPBYTE                lpMemory;  
  16. };  
  17.   
  18. //内存池标头结构   
  19. typedef struct tagHEADER  HEADER, *LPHEADER;  
  20. struct tagHEADER  
  21. {  
  22.     LPMEMORY_BLOCK   lpHead;  
  23.     HANDLE           hHeap;  
  24. };  
  25.   
  26. //内存池对象构造函数   
  27. CMemPool::CMemPool()  
  28. {  
  29.     handle = NULL;  
  30. }  
  31.   
  32. //内存池对象析构函数   
  33. CMemPool::~CMemPool()  
  34. {  
  35.     Destroy();  
  36. }  
  37.   
  38. //内存池对象初始化,首次分配8KB内存作为内存池   
  39. BOOL CMemPool::Initialize()  
  40. {  
  41.     if( NULL == handle )  
  42.     {  
  43.         HANDLE  procHeap = GetProcessHeap();  
  44.   
  45.         // 分配内存池的头节点.   
  46.         handle = HeapAlloc(procHeap, 0, sizeof(HEADER));  
  47.   
  48.         if (handle)   
  49.         {  
  50.             LPHEADER header = (LPHEADER)handle;  
  51.             // 分配头节点成功,现在初始化内存池.   
  52.             header->lpHead = NULL;  
  53.             header->hHeap = procHeap;  
  54.   
  55.             //初次实际分配8KB内存到内存池.   
  56.             BOOL  ableToAddMemory = AddMemory(0);  
  57.   
  58.             if (!ableToAddMemory)   
  59.             {  
  60.                 //分配内存失败,系统资源瓶颈!   
  61.                 HeapFree(header->hHeap, 0, handle);  
  62.                 handle = NULL;  
  63.             }  
  64.         }  
  65.     }  
  66.     return (handle != NULL);  
  67. }  
  68.   
  69. VOID CMemPool::Destroy()  
  70. {  
  71.     if(handle != NULL)  
  72.     {  
  73.         LPMEMORY_BLOCK  nextBlock;  
  74.         LPMEMORY_BLOCK  blockToFree;   
  75.         LPHEADER        poolHeader = (LPHEADER)handle;  
  76.   
  77.         //遍历链表,进行释放内存操作.   
  78.         blockToFree = poolHeader->lpHead;  
  79.   
  80.         while (blockToFree != NULL)  
  81.         {  
  82.             nextBlock = blockToFree->lpNext;  
  83.             HeapFree(poolHeader->hHeap, 0, blockToFree);  
  84.             blockToFree = nextBlock;  
  85.         }  
  86.   
  87.         //别忘记了,内存池头结点也需要释放.   
  88.         HeapFree(poolHeader->hHeap, 0, handle);  
  89.         handle = NULL;  
  90.     }  
  91. }  
  92.   
  93. LPVOID CMemPool::GetAlignedMemory(DWORD dwSize, DWORD dwAlignSize)  
  94. {  
  95.     assert(handle != NULL);  
  96.   
  97.     BOOL            haveEnoughMemory = TRUE;  
  98.     LPVOID          lpMemory         = NULL;  
  99.     LPHEADER        poolHeader       = (LPHEADER)handle;  
  100.     LPMEMORY_BLOCK  currentBlock;  
  101.     DWORD           sizeNeeded;  
  102.     DWORD           padLength;  
  103.   
  104.     currentBlock = poolHeader->lpHead;  
  105.   
  106.     // 判断是否需要更多的内存,如果是,则分配之.   
  107.     sizeNeeded = dwSize;  
  108.   
  109.     if (currentBlock->dwSize - currentBlock->dwIndex < sizeNeeded + dwAlignSize)  
  110.     {  
  111.         haveEnoughMemory = AddMemory(sizeNeeded + dwAlignSize);  
  112.         currentBlock = poolHeader->lpHead;  
  113.     }  
  114.   
  115.     // 现在有了足够的内存,返回它!   
  116.     if (haveEnoughMemory)  
  117.     {  
  118.         if (dwAlignSize)  
  119.         {  
  120.             padLength = (DWORD)currentBlock + sizeof(MEMORY_BLOCK) + currentBlock->dwIndex;  
  121.             currentBlock->dwIndex += (dwAlignSize - (padLength % dwAlignSize)) % dwAlignSize;  
  122.         }  
  123.           
  124.         //这里得到了内存地址,返回它!   
  125.         lpMemory = (LPVOID)&(currentBlock->lpMemory[currentBlock->dwIndex]);  
  126.   
  127.         currentBlock->dwIndex += sizeNeeded;  
  128.     }  
  129.   
  130.     return lpMemory;  
  131. }  
  132.   
  133. LPSTR CMemPool::GetDuplicateStringA(LPCSTR szSrc)  
  134. {  
  135.     assert(szSrc);  
  136.   
  137.     DWORD dwBytes = (_mbslen((const unsigned char*)szSrc) + 1) * sizeof(CHAR);  
  138.     LPSTR pString = (LPSTR)GetAlignedMemory(dwBytes, sizeof(CHAR));  
  139.   
  140.     if (pString)   
  141.     {  
  142.         _mbscpy_s((unsigned char*)pString, dwBytes, (const unsigned char*)szSrc);  
  143.     }  
  144.     return pString;  
  145. }  
  146.   
  147. LPWSTR CMemPool::GetDuplicateStringW(LPCWSTR szSrc)  
  148. {  
  149.     assert(szSrc);  
  150.   
  151.     DWORD dwBytes = (wcslen(szSrc) + 1) * sizeof(WCHAR);  
  152.     LPWSTR pString = (LPWSTR)GetAlignedMemory(dwBytes, sizeof(WCHAR));  
  153.   
  154.     if (pString)   
  155.     {  
  156.         wcscpy_s(pString, dwBytes, szSrc);  
  157.     }  
  158.     return pString;  
  159. }  
  160.   
  161. BOOL CMemPool::AddMemory(DWORD dwSize)  
  162. {  
  163.     LPBYTE           allocedMemory;  
  164.     LPMEMORY_BLOCK   newBlock;  
  165.     LPHEADER         poolHeader = (LPHEADER)handle;  
  166.     DWORD            sizeNeeded;  
  167.   
  168.     assert(poolHeader != NULL);  
  169.   
  170.     // 计算需要分配内存的最小数量,并试图分配之.   
  171.     if (dwSize + sizeof(MEMORY_BLOCK) > MEMPOOL_BLOCK_SIZE)   
  172.     {  
  173.         sizeNeeded = dwSize + sizeof(MEMORY_BLOCK);  
  174.     }  
  175.     else   
  176.     {  
  177.         sizeNeeded = MEMPOOL_BLOCK_SIZE;  
  178.     }  
  179.   
  180.     allocedMemory = (LPBYTE)HeapAlloc(poolHeader->hHeap, 0, sizeNeeded);  
  181.   
  182.     if (allocedMemory)   
  183.     {  
  184.         // 使内存块的头部存储一个MEMORY_BLOCK结构.   
  185.         newBlock = (LPMEMORY_BLOCK)allocedMemory;  
  186.         newBlock->dwSize = sizeNeeded - sizeof(MEMORY_BLOCK);  
  187.         newBlock->lpMemory = allocedMemory + sizeof(MEMORY_BLOCK);  
  188.         newBlock->dwIndex = 0;  
  189.       
  190.         // 把内存块链接到list中.   
  191.         if(poolHeader->lpHead)   
  192.         {  
  193.             poolHeader->lpHead->lpPrev = newBlock;  
  194.         }  
  195.         newBlock->lpNext = poolHeader->lpHead;  
  196.         newBlock->lpPrev = NULL;  
  197.         poolHeader->lpHead = newBlock;  
  198.     }  
  199.   
  200.     // 如果allocedMemory 不是 NULL, 则表明我们成功了.   
  201.     return allocedMemory != NULL;  
  202. }  

 

CMemPool类使用演示程序代码如下:

 

view plaincopy to clipboardprint?
  1. #include <TCHAR.H>   
  2. #include "MemPool.h"   
  3.   
  4. int main()  
  5. {  
  6.     CMemPool mp;  
  7.     assert( mp.Initialize() );  
  8.   
  9.     for(int i = 0; i<100; i++)  
  10.     {  
  11.         TCHAR* psz = mp.GetStringBuffer(8192);  
  12.         _stprintf_s(psz, 8192, TEXT("now i=%d/n"), i);  
  13.         _tprintf(psz);  
  14.     }  
  15.   
  16.     mp.Destroy();  
  17.     return getchar();  
  18. }  

 

在其中的_tprintf(psz);和mp.Destroy();这两行打上断点,调试运行,观察windows任务管理器关于该进程的内存使用量呈明显的规律性增长。最后当执行完mp.Destroy();后该进程的内存使用量又恢复到未使用内存池时的状态

原创粉丝点击