文档章节

C++内存池实现

zhangyujsj
 zhangyujsj
发布于 2015/07/11 20:00
字数 1559
阅读 24
收藏 0

利用C/C++开发大型应用程序中,内存的管理与分配是一个需要认真考虑的部分。

本文描述了内存池设计原理并给出内存池的实现代码,代码支持Windows和Linux,多线程安全。

内存池设计过程中需要考虑好内存的分配与释放问题,其实也就是空间和时间的矛盾。

有的内存池设计得很巧妙,内存分配与需求相当,但是会浪费过多的时间去查找分配与释放,这就得不偿失;

实际使用中,我们更多的是关心内存分配的速度,而不是内存的使用效率。基于此,本文按照如下思想设计实现内存池。

主要包含三个结构:StiaticMemory, MemoryChunk和MemoryBlock,三者之间的关系如下图所示:



1.内存的分配:

(1)如果分配大小超过1024,直接采用malloc分配,分配的时候多分配sizeof(size_t)字节,用于保存该块的大小;

(2)否则根据分配大小,查找到容纳该大小的最小size的MemoryChunk;

(3)查找MemoryChunk的链表指针pList,找到空闲的MemoryBlock返回;

(4)如果pList为NULL,临时创建MemoryBlock返回;

(5)MemoryBlock头部包含两个成员,pChunk指向的所属的MemoryChunk对象,size表明大小,其后才是给用户使用的空间;

2.内存的释放:

(1)根据释放的指针,查找器size头部,即减去sizeof(size_t)字节,判断该块的大小;

(2)如果大小超过1024,直接free;

(3)否则交给MemoryChunk处理,而块的头部保存了该指针,因此直接利用该指针就可以收回该内存。

注意的问题:

上述设计的内存池通过冗余的头部来实现内存块的分配与释放,减少了内存池的操作时间,速度上要优于原始的malloc和free操作,同时减少了内存碎片的增加。

但是该设计中没有去验证释放的块冗余头部的正确性,因此故意释放不属于内存池中的块或者修改头部信息都会导致内存池操作失败,当然这些可以由程序员来控制。

此外,内存池中分配出去的内存块如果不主动释放,内存池没有保留信息,不会自动释放,但是在退出的时候会验证验证是否完全释放,其实这个在系统测试时候就可以检测出来,我想这个缺陷也是可以弥补的,在此提出,希望使用者注意。

下面贴上源码,如果对代码有任何建议或者发现存在的Bug,希望与我联系,共同学习交流,Tx。


MemoryChunk.h 文件,线程安全

[html]  view plain copy
  1. #ifndef MEMORY_CHUNK_H  
  2. #define MEMORY_CHUNK_H  
  3. #include <cstdio>  
  4. #include <cassert>  
  5. #include <cstdlib>  
  6.   
  7. #ifdef WIN32  
  8. #include <windows.h>  
  9. typedef CRITICAL_SECTION MUTEXTYPE;  
  10. #define INITMUTEX(hMutex) InitializeCriticalSection(&hMutex)  
  11. #define DELMUTEX(hMutex) DeleteCriticalSection(&hMutex)  
  12. #define LOCK(hMutex) EnterCriticalSection(&hMutex)  
  13. #define UNLOCK(hMutex) LeaveCriticalSection(&hMutex)  
  14. #else  
  15. #include <pthread.h>  
  16. typedef pthread_mutex_t MUTEXTYPE;  
  17. #define INITMUTEX(hMutex) pthread_mutex_init(&hMutex,NULL)  
  18. #define DELMUTEX(hMutex) pthread_mutex_destroy(&hMutex)  
  19. #define LOCK(hMutex) pthread_mutex_lock(&hMutex)  
  20. #define UNLOCK(hMutex) pthread_mutex_unlock(&hMutex)  
  21. #endif  
  22.   
  23. class MemoryChunk;  
  24.   
  25. /** @struct MemoryBlock  
  26.  *  
  27.  */  
  28. struct BlockHeader  
  29. {  
  30.     MemoryChunk* pChunk;  
  31.     size_t len;  
  32. };  
  33. struct MemoryBlock;  
  34. struct BlockData  
  35. {  
  36.     union{  
  37.         MemoryBlock* pNext;  
  38.         char pBuffer;  
  39.     };  
  40. };  
  41. struct MemoryBlock  
  42. {  
  43.     BlockHeader header;  
  44.     BlockData data;  
  45. };  
  46.   
  47. /** @class MemoryChunk  
  48.  *  
  49.  */  
  50.   
  51. class MemoryChunk  
  52. {  
  53. public:  
  54.     MemoryChunk(size_t size, int count)  
  55.     {  
  56.         INITMUTEX(hMutex);  
  57.         this->pFreeList=NULL;  
  58.         this->size=size;  
  59.         this->count=0;  
  60.         MemoryBlock* pBlock;  
  61.         while(count--){  
  62.             pBlock=CreateBlock();  
  63.             if(!pBlock)break;  
  64.             pBlock->data.pNext=pFreeList;  
  65.             pFreeList=pBlock;  
  66.         }  
  67.     }  
  68.     ~MemoryChunk()  
  69.     {  
  70.         int tempcount=0;  
  71.         MemoryBlock* pBlock;  
  72.         while(pBlock=pFreeList){  
  73.             pFreeList=pBlock->data.pNext;  
  74.             DeleteBlock(pBlock);  
  75.             ++tempcount;  
  76.         }  
  77.         assert(tempcount==count);//!确保释放完全  
  78.         DELMUTEX(hMutex);  
  79.     }  
  80.     void* malloc()  
  81.     {  
  82.         MemoryBlock* pBlock;  
  83.         LOCK(hMutex);  
  84.         if(pFreeList){  
  85.             pBlock=pFreeList;  
  86.             pFreeList=pBlock->data.pNext;  
  87.         }  
  88.         else{  
  89.             if(!(pBlock=CreateBlock())){  
  90.                 UNLOCK(hMutex);  
  91.                 return NULL;  
  92.             }  
  93.         }  
  94.         UNLOCK(hMutex);  
  95.         return &pBlock->data.pBuffer;  
  96.     }  
  97.     static void free(void* pMem)  
  98.     {  
  99.         MemoryBlock* pBlock=(MemoryBlock*)((char*)pMem-sizeof(BlockHeader));  
  100.         pBlock->header.pChunk->free(pBlock);  
  101.     }  
  102.     void free(MemoryBlock* pBlock)  
  103.     {  
  104.         LOCK(hMutex);  
  105.         pBlock->data.pNext=pFreeList;  
  106.         pFreeList=pBlock;  
  107.         UNLOCK(hMutex);  
  108.     }  
  109.       
  110.     MemoryChunk* Next(){return pNext;}  
  111.   
  112. protected:  
  113.     MemoryBlock* CreateBlock()  
  114.     {  
  115.         MemoryBlock* pBlock=(MemoryBlock*)::malloc(sizeof(BlockHeader)+size);  
  116.   
  117.         if(pBlock){  
  118.   
  119.             pBlock->header.pChunk=this;  
  120.             pBlock->header.len=size;  
  121.               
  122.             ++count;  
  123.         }  
  124.         return pBlock;  
  125.     }  
  126.     void DeleteBlock(MemoryBlock* pBlock)  
  127.     {  
  128.         ::free(pBlock);  
  129.     }  
  130. private:  
  131.     MemoryBlock* pFreeList;  
  132.     size_t size;//!Block大小  
  133.     int count;//!Block数目  
  134.     MemoryChunk* pNext;  
  135.     MUTEXTYPE hMutex;  
  136. };  
  137. #endif  
StaticMemory.h文件,内存池对象
[html]  view plain copy
  1. #ifndef STATIC_MEMORY_H  
  2. #define STATIC_MEMORY_H  
  3. #include "MemoryChunk.h"  
  4. /** @ StaticMemory.h  
  5.  * 定义实现内存池  
  6.  * 采用固定大小策略进行内存管理与分配  
  7.  * 减少因大量小内存分配导致的内存碎片增加  
  8.  */  
  9. struct HeapHeader  
  10. {  
  11.     size_t size;  
  12. };  
  13. struct MemoryHeap  
  14. {  
  15.     HeapHeader header;  
  16.     char pBuffer;  
  17. };  
  18.   
  19. class StaticMemory  
  20. {  
  21. public:  
  22.     typedef enum{MAX_SIZE=1024,MIN_SIZE=sizeof(MemoryChunk*)};  
  23.     StaticMemory()  
  24.     {  
  25.         chunkcount=0;  
  26.         for(size_t size=MIN_SIZE; size<=MAX_SIZE; size*=2)++chunkcount;  
  27.         //pChunkList=(MemoryChunk**)malloc(sizeof(MemoryChunk*)*chunkcount);  
  28.         pChunkList=new MemoryChunk*[chunkcount];  
  29.         int index=0;  
  30.         for(size_t size=MIN_SIZE; size<=MAX_SIZE; size*=2)  
  31.         {  
  32.             pChunkList[index++]=new MemoryChunk(size,1000);  
  33.         }  
  34.     }  
  35.     ~StaticMemory()  
  36.     {  
  37.         for(int index=0; index<chunkcount; ++index)  
  38.         {  
  39.             delete pChunkList[index];  
  40.         }  
  41.         //free(pChunkList);  
  42.         delete[] pChunkList;  
  43.     }  
  44.     void* Malloc(size_t size)  
  45.     {  
  46.         if(size>MAX_SIZE){  
  47.             return malloc(size);  
  48.         }  
  49.         int index=0;  
  50.         for(size_t tsize=MIN_SIZE; tsize<=MAX_SIZE; tsize*=2){  
  51.             if(tsize>=size)break;  
  52.             ++index;  
  53.         }  
  54.         return pChunkList[index]->malloc();  
  55.     }  
  56.     void Free(void* pMem)  
  57.     {  
  58.         if(!free(pMem))MemoryChunk::free(pMem);  
  59.     }  
  60. protected:  
  61.     void* malloc(size_t size)  
  62.     {  
  63.         MemoryHeap* pHeap=(MemoryHeap*)::malloc(sizeof(HeapHeader)+size);  
  64.         if(pHeap){  
  65.             pHeap->header.size=size;  
  66.             return &pHeap->pBuffer;  
  67.         }  
  68.         return NULL;  
  69.     }  
  70.     bool free(void* pMem)  
  71.     {  
  72.         MemoryHeap* pHeap=(MemoryHeap*)((char*)pMem-sizeof(HeapHeader));  
  73.         if(pHeap->header.size>MAX_SIZE){  
  74.             ::free(pHeap);  
  75.             return true;  
  76.         }  
  77.         return false;  
  78.     }  
  79. private:  
  80.     MemoryChunk** pChunkList;  
  81.     int chunkcount;  
  82. };  
  83. #endif  
ObejctManager.h文件,用于实现对象的创建与管理,比较简易。
[html]  view plain copy
  1. #ifndef OBJECT_MANAGER_H  
  2. #define OBJECT_MANAGER_H  
  3. #include "StaticMemory.h"  
  4. /** @class ObjectManager  
  5.  * 实现利用内存池创建对象  
  6.  * 要求对象具有缺省构造函数  
  7.  */  
  8. template<typename T>  
  9. class ObjectManager  
  10. {  
  11. public:  
  12.     typedef T ObjectType;  
  13.   
  14.     static ObjectType* Create(StaticMemory* pool)  
  15.     {  
  16.         void* pobject=pool->Malloc(sizeof(T));  
  17.         new(pobject) ObjectType();  
  18.         return static_cast<ObjectType*>(pobject);  
  19.     }  
  20.     static void Delete(StaticMemory* pool, ObjectType* pobject)  
  21.     {  
  22.         pobject->~ObjectType();  
  23.         pool->Free(pobject);  
  24.     }  
  25. };  
  26. #endif  
测试结果:

分单线程和多线程进行测试,重复的内存分配与释放在实际使用中是不太可能的,为了模拟实际使用,通过随机数来确定分配内存大小,同时也通过随机数来确定分配与释放操作。在测试过程中限制最大分配大小为1024,目的是为了测试小内存块的分配情况对比。

内存池单线程测试结果
分配与释放次数 malloc/free 内存池
                                                        100,000             0.01s         0.01s
                                                      1,000,000             0.15s         0.11s
                                                     10,000,000             1.26s         0.60s
                                                    100,000,000             9.21s         5.99s
                                                  1,000,000,000             92.70s         61.46s


内存池多线程测试结果
   线程数目                 malloc/free                       内存池
1/1,000,000                   0.15s                       0.10s
2/1,000,000                  1.49s                       0.73s
4/1,000,000                  9.945s                       6.71s
8/1,000,000                  45.60s                      28.82s
进行多线程测试主要是测试多线程运行下,加锁给内存分配带来的影响,因此为了排除CPU的影响,测试采用的机器为16盒,16G内存的Linux服务器。

具体配置如下:

Intel(R) Xeon(R) CPU           E5630  @ 2.53GHz

stepping        : 2
cpu MHz         : 2527.084

cache size      : 12288 KB

本文转载自:http://blog.csdn.net/neustar1/article/details/7478311

上一篇: C++内存管理
zhangyujsj
粉丝 24
博文 358
码字总数 224241
作品 0
广州
私信 提问
acl 网络通信服务器框架 3.1.0 版本发布

acl 3.1.0 版本发布了,acl 是 one advanced C/C++ library 的简称,主要包括网络通信库以及服务器框架库等功能,支持 Linux/Windows/Solaris/FreeBsd/MacOS 平台;整个 acl 项目主要包含三个...

郑树新
2015/02/08
3K
2
功能强大的 C++ redis 客户端库增加至 acl 项目中

虽然 redis 开发库已有不少,但 C/C++ 的客户端库好用的并不多,虽然官方也提供了 C 版的客户端库,但易用性较差,而且不支持连接池功能,相对于 C/C++ 的库,JAVA 版的 jedis 要好用的多,j...

郑树新
2015/02/04
8.6K
9
UCommon 4.1.0 发布

UCommon 是一个轻量级的 C++ 库,使用 C++ 设计模式,适合用于嵌入式应用的开发,例如使用 uClibc 和 POSIX 线程支持。为了这个目的,UCommon 禁用了一些特别消耗内存的语言特性,引入了 Ob...

红薯
2011/01/23
529
0
关于Nebula3工程的几个编译选项

研究一下人家是怎么通过编译选项来优化性能的 DEBUG: C++/Code Generation/Enable String Pooling: Yes (/GF) 该选项使编译器能够为执行过程中程序映像和内存中的相同字符串创建单个副本,从...

长平狐
2012/11/12
87
0
网络与服务器编程框架库 acl_3.0.13 发布

acl 3.0.13 版本 (项目主页:https://sourceforge.net/projects/acl/) 发布了,acl 是 one Advanced C/C++ library 的简称,主要包括网络通信库以及服务器框架库等功能,支持 Linux/Windows ...

郑树新
2013/11/04
1K
6

没有更多内容

加载失败,请刷新页面

加载更多

vue入门--简单路由配置

本文转载于:专业的前端网站➜vue入门--简单路由配置   在初始化vue init webpack <工程名>时,有一步是询问是否安装vue-router,选择yes,如果没有安装的话,后面需要自己安装。然后在目录...

前端老手
33分钟前
3
0
怎么给视频配音

很多刚开始尝试视频制作的小伙伴,帮助到怎么给制作完成的视频配音,其实给视频配音的方法非常简单,在手机上可以进行制作,下面一起来看看给视频配音的方法吧! 具体步骤如下: 1、首先在手...

白米稀饭2019
42分钟前
3
0
windows批处理bat脚本编写

什么是bat脚本 .bat结尾的文件其实就是windows上的批处理脚本,Windows中的bat文件相当于 Linux中shell编程的.sh脚本,批量执行DOS命令。 其最简单的例子,是逐行书写在命令行中会用到的各种...

孙幼凌
50分钟前
3
0
华为手机翻译功能怎么使用?这三种方法请务必收藏

华为手机翻译功能怎么使用?在我们的生活中会经常遇到翻译问题,许多外语不好的朋友该怎么办呢?华为手机已经为我们解决了这个问题,今天小编就教大家学会使用华为手机中的三种翻译技巧,需要...

翻译小天才
58分钟前
5
0
企业服务软件开发中需要注意的三个问题

在开发企业服务软件时,我们需要分为:业务需求、用户需求、产品需求,三大需求层次,三个层次互相关联,企业服务软件开发首先要服务业务,需要满足业务的需求,再关注用户体验,也就是用户需...

积木创意科技
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部