c++内存池实现

  利用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操作,同时减少了内存碎片的增加。但是该设计中没有去验证释放的块冗余头部的正确性,因此故意释放不属于内存池中的块或者修改头部信息都会导致内存池操作失败,当然这些可以由程序员来控制。此外,内存池中分配出去的内存块如果不主动释放,内存池没有保留信息,不会自动释放,但是在退出的时候会验证验证是否完全释放,其实这个在系统测试时候就可以检测出来,我想这个缺陷也是可以弥补的,在此提出,希望使用者注意。

MemoryChunk.h 文件,线程安全

  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文件,内存池对象

 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文件,用于实现对象的创建与管理,比较简易。

 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

时间: 2024-10-08 16:57:19

c++内存池实现的相关文章

内存池、进程池、线程池

首先介绍一个概念"池化技术 ".池化技术 一言以蔽之就是:提前保存大量的资源,以备不时之需以及重复使用. 池化技术应用广泛,如内存池,线程池,连接池等等.内存池相关的内容,建议看看Apache.Nginx等开源web服务器的内存池实现. 起因:由于在实际应用当中,分配内存.创建进程.线程都会设计到一些系统调用,系统调用需要导致程序从用户态切换到内核态,是非常耗时的操作.           因此,当程序中需要频繁的进行内存申请释放,进程.线程创建销毁等操作时,通常会使用内存池.进程池.

详谈内存管理技术(二)、内存池

嗯,这篇讲可用的多线程内存池. 零.上期彩蛋:不要重载全局new 或许,是一次很不愉快的经历,所以在会有这么一个"认识".反正,大概就是:即使你足够聪明,也不要自作聪明:在这就是不要重载全局new,无论你有着怎样的目的和智商.因为: class XXX{ public: XXX* createInstance(); }; 这是一个不对称的接口:只告诉了我们如何创建一个[堆]对象,但是释放呢??! 很无奈,只能假设其使用全局默认的delete来删除(除此之外,没有其他选择):这时,我为了

InnoDB 存储引擎的线程与内存池

InnoDB 存储引擎的线程与内存池 InnoDB体系结构如下: 后台线程: 1.后台线程的主要作用是负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据: 2.另外,将以修改的数据文件刷新到磁盘文件: 3.同时,保证在数据库发生异常的情况下,InnoDB能恢复到正常运行状态. 内存池:InnoDB有多个内存块,这些内存块组成了一个大的内存池.这些内存块包括有:缓冲池(innodb_buffer_pool)和日志缓冲(log_buffer)以及额外内存池(innodb_addtional

内存池技术介绍(图文并茂,非常清楚)

看到一篇关于内存池技术的介绍文章,受益匪浅,转贴至此. 原贴地址:http://www.ibm.com/developerworks/cn/linux/l-cn-ppp/index6.html 6.1 自定义内存池性能优化的原理 如前所述,读者已经了解到"堆"和"栈"的区别.而在编程实践中,不可避免地要大量用到堆上的内存.例如在程序中维护一个链表的数据结构时,每次新增或者删除一个链表的节点,都需要从内存堆上分配或者释放一定的内存:在维护一个动态数组时,如果动态数组的

基于C/S架构的3D对战网络游戏C++框架 _05搭建系统开发环境与Boost智能指针、内存池初步了解

本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): 1.实现基本通信框架,包括对游戏的需求分析.设计及开发环境和通信框架的搭建: 2.实现网络底层操作,包括创建线程池.序列化网络包等: 3.实战演练,实现类似于CS反恐精英的3D对战网络游戏: 技术要点:C++面向对象思想.网络编程.Qt界面开发.Qt控件知识.Boost智能指针.STL算法.STL.

重写boost内存池

最近在写游戏服务器网络模块的时候,需要用到内存池.大量玩家通过tcp连接到服务器,通过大量的消息包与服务器进行交互.因此要给每个tcp分配收发两块缓冲区.那么这缓冲区多大呢?通常游戏操作的消息包都很小,大概几十字节.但是在玩家登录时或者卡牌游戏发战报(将整场战斗打完,生成一个消息包),包的大小可能达到30k或者更大,取决于游戏设定.这些缓冲区不可能使用glibc原始的new.delete来分配,这样可能会造成严重的内存碎片,并且效率也不高. 于是我们要使用内存池.并且是等长内存池,即每次分配的内

Innodb额外内存池的分配策略以及性能

Innodb额外内存池的分配策略以及性能 作者:明天会更好 QQ:715169549 备注:未经同意,严禁转载,谢谢合作. //内存池结构体 /** Data structure for a memory pool. The space is allocated using the buddy algorithm, where free list i contains areas of size 2 to power i. */ struct mem_pool_t{ byte* buf; /*!

[原创]loki库之内存池SmallObj

loki库之内存池SmallObj 介绍 loki库的内存池实现主要在文件smallobj中,顾名思义它的优势主要在小对象的分配与释放上,loki库是基于策略的方法实现的,简单的说就是把某个类通过模板参数传递给主类,比如某个对象的创建可以通过不同的创建策略进行创建,本文主要讲loki的大致实现. smallobj层次 loki.smallobj主要分四层: 应用层smallobject,重载了operator new 和operator delete,内存通过底层获取 内存分配smallobjA

【核心基础】内存池

本节将研究Nginx关于内存申请与释放的核心代码: 基本示意图 内存池对象初始状态 小内存申请后状态 大内存申请后状态 核心代码分析 核心结构体声明 //大内存管理结构 struct ngx_pool_large_s { ngx_pool_large_t *next; //连接下一个大内存管理 void *alloc; //申请的大内存地址 }; //内存池中数据管理 typedef struct { u_char *last; //可用内存的起始地址 u_char *end; //可用内存的末

简单的内存池实现gko_alloc

在用gpreftools优化gko_pool的时候我发现一个问题,malloc竟然成了性能瓶颈 由于在每个连接建立的时候gko_pool默认会为读写各分配2KB的buf备用,这个是比较固定的 每个连接的的生命周期会伴随着4KB大小的内存malloc & free 正好可以写个只能分配固定大小内存的"内存池",基本思路就是每次分配一个大内存bucket(64MB),需要4KB的块的时候就从bucket中取,当bucket没有可用slot就再分配一个新的bucket,当bucket