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

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

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

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

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

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

  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. typedef struct tagHEADER  HEADER, *LPHEADER;
  13. struct tagHEADER
  14. {
  15.     LPMEMORY_BLOCK   lpHead;
  16.     HANDLE           hHeap;
  17. };

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

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

  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. #define STRICT
  8. #define LEAN_AND_MEAN
  9. #include <windows.h>
  10. #include <assert.h>
  11. #include <stdio.h>
  12. #include <mbstring.h>
  13. //默认内存分配页大小设为8KB
  14. #define     MEMPOOL_BLOCK_SIZE      8192
  15. class CMemPool
  16. {
  17. public:
  18.     CMemPool();
  19.     ~CMemPool();
  20.     BOOL   Initialize();
  21.     VOID   Destroy();
  22.     LPVOID GetAlignedMemory(DWORD dwSize, DWORD dwAlignSize);
  23.     LPSTR  GetDuplicateStringA(LPCSTR szSrc);
  24.     LPWSTR GetDuplicateStringW(LPCWSTR szSrc);
  25.     inline LPVOID GetMemory(DWORD dwSize) 
  26.     {
  27.         return GetAlignedMemory(dwSize, 0);
  28.     }
  29.     inline TCHAR* GetStringBuffer(DWORD dwLen)
  30.     {
  31.         return (TCHAR*)GetAlignedMemory(dwLen * sizeof(TCHAR), 0);
  32.     }
  33.     inline LPSTR GetStringBufferA(DWORD dwLen) 
  34.     {
  35.         return (LPSTR)GetAlignedMemory(dwLen * sizeof(CHAR), sizeof(CHAR));
  36.     }
  37.     inline LPWSTR GetStringBufferW(DWORD dwLen) 
  38.     {
  39.         return (LPWSTR)GetAlignedMemory(dwLen * sizeof(WCHAR), sizeof(WCHAR));
  40.     }
  41.     inline DWORD* GetDwordBuffer()
  42.     {
  43.         return (DWORD*)GetAlignedMemory(sizeof(DWORD), 0);
  44.     }
  45. private:
  46.     BOOL   AddMemory(DWORD dwSize);
  47.     LPVOID handle;
  48. };
  49. #endif //MEMPOOL_H

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

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

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

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

在其中的

  1. _tprintf(psz);

  1. mp.Destroy();

这两行打上断点,调试运行,观察windows任务管理器关于该进程的内存使用量呈明显的规律性增长。最后当执行完

mp.Destroy();后该进程的内存使用量又恢复到未使用内存池时的状态。

下载时请看下面说明,对写一个动态的内存池很有帮助。 这是一个C++语言链表的方法实现一个静态内存池代源码。原理就是先向系统申请一块大内存,然后把这一大块分隔成相等的很多小块,然后在这这些小块的首地址部份放一个结构体,结构体中有一个值是用来标明这一小块是否使用中。在这里存放你要放存的数据就是用结构体首地址加结构体自身长度就得到要放数据的首地址了.具体看代码的实现吧。我说一下动态内存池的写法。那是我给公司写的就不能上传了。结构体和静态内存池的这个差不多一样,只是增加了一个成员用来记录每一节点到大块内存的首地址在到本节点的一个尺寸长度值,做法也是先申请一块大内存。我先从释放说起吧,释放本节点时看自己的相邻节点是不是有释放掉的,如果有则合并掉他们成为一个块,如果碰到相邻的节点是另外的一个大块的话就不用合并了,原因他和自己所在的这一个大块内存上物理地址不是连续,这里一定要记住,释放过程算法怎么去写就看你的了。下面是分配写法要考虑的。在分配一小块内存给高层使用时,如果是分配在尾节点去分配的情况,那好办啊,尾节点如果不够分配了就直接从系统去申请一块大内存,节点连起来在分配,这里有可能会浪费掉一小块以结构体大小的一块内存,如果够分配就直接分配了。如果是在中间节点去分配,这里就要将释放时合并的如果大于现在要分配的就拆开来用,如果拆开剩余的那一部份只有结构体大小就不用在拆开了。这些都是要考虑的东西,优化加快速度就看你自己了.可能看时不些不明白,看静态内存的写法后你就明白了.有时我也要下载其他人共享的东西,所以就一分吧.哈哈~~~~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值