C++内存池实现

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

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

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

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

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

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


C++内存池实现


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 文件,线程安全
#ifndef MEMORY_CHUNK_H
#define MEMORY_CHUNK_H
#include <cstdio>
#include <cassert>
#include <cstdlib>
#ifdef WIN32
#include <windows.h>
typedef CRITICAL_SECTION MUTEXTYPE;
#define INITMUTEX(hMutex) InitializeCriticalSection(&hMutex)
#define DELMUTEX(hMutex) DeleteCriticalSection(&hMutex)
#define LOCK(hMutex) EnterCriticalSection(&hMutex)
#define UNLOCK(hMutex) LeaveCriticalSection(&hMutex)
#else
#include <pthread.h>
typedef pthread_mutex_t MUTEXTYPE;
#define INITMUTEX(hMutex) pthread_mutex_init(&hMutex,NULL)
#define DELMUTEX(hMutex) pthread_mutex_destroy(&hMutex)
#define LOCK(hMutex) pthread_mutex_lock(&hMutex)
#define UNLOCK(hMutex) pthread_mutex_unlock(&hMutex)
#endif
class MemoryChunk;
/** @struct MemoryBlock
 *
 */
struct BlockHeader
{
MemoryChunk* pChunk;
size_t len;
};
struct MemoryBlock;
struct BlockData
{
union{
MemoryBlock* pNext;
char pBuffer;
};
};
struct MemoryBlock
{
BlockHeader header;
BlockData data;
};
/** @class MemoryChunk
 *
 */
class MemoryChunk
{
public:
MemoryChunk(size_t size, int count)
{
INITMUTEX(hMutex);
this->pFreeList=NULL;
this->size=size;
this->count=0;
MemoryBlock* pBlock;
while(count--){
pBlock=CreateBlock();
if(!pBlock)break;
pBlock->data.pNext=pFreeList;
pFreeList=pBlock;
}
}
~MemoryChunk()
{
int tempcount=0;
MemoryBlock* pBlock;
while(pBlock=pFreeList){
pFreeList=pBlock->data.pNext;
DeleteBlock(pBlock);
++tempcount;
}
assert(tempcount==count);//!确保释放完全
DELMUTEX(hMutex);
}
void* malloc()
{
MemoryBlock* pBlock;
LOCK(hMutex);
if(pFreeList){
pBlock=pFreeList;
pFreeList=pBlock->data.pNext;
}
else{
if(!(pBlock=CreateBlock())){
UNLOCK(hMutex);
return NULL;
}
}
UNLOCK(hMutex);
return &pBlock->data.pBuffer;
}
static void free(void* pMem)
{
MemoryBlock* pBlock=(MemoryBlock*)((char*)pMem-sizeof(BlockHeader));
pBlock->header.pChunk->free(pBlock);
}
void free(MemoryBlock* pBlock)
{
LOCK(hMutex);
pBlock->data.pNext=pFreeList;
pFreeList=pBlock;
UNLOCK(hMutex);
}
MemoryChunk* Next(){return pNext;}
protected:
MemoryBlock* CreateBlock()
{
MemoryBlock* pBlock=(MemoryBlock*)::malloc(sizeof(BlockHeader)+size);
if(pBlock){
pBlock->header.pChunk=this;
pBlock->header.len=size;
++count;
}
return pBlock;
}
void DeleteBlock(MemoryBlock* pBlock)
{
::free(pBlock);
}
private:
MemoryBlock* pFreeList;
size_t size;//!Block大小
int count;//!Block数目
MemoryChunk* pNext;
MUTEXTYPE hMutex;
};
#endif
StaticMemory.h文件,内存池对象
#ifndef STATIC_MEMORY_H
#define STATIC_MEMORY_H
#include "MemoryChunk.h"
/** @ StaticMemory.h
 * 定义实现内存池
 * 采用固定大小策略进行内存管理与分配
 * 减少因大量小内存分配导致的内存碎片增加
 */
struct HeapHeader
{
size_t size;
};
struct MemoryHeap
{
HeapHeader header;
char pBuffer;
};
class StaticMemory
{
public:
typedef enum{MAX_SIZE=1024,MIN_SIZE=sizeof(MemoryChunk*)};
StaticMemory()
{
chunkcount=0;
for(size_t size=MIN_SIZE; size<=MAX_SIZE; size*=2)++chunkcount;
//pChunkList=(MemoryChunk**)malloc(sizeof(MemoryChunk*)*chunkcount);
pChunkList=new MemoryChunk*[chunkcount];
int index=0;
for(size_t size=MIN_SIZE; size<=MAX_SIZE; size*=2)
{
pChunkList[index++]=new MemoryChunk(size,1000);
}
}
~StaticMemory()
{
for(int index=0; index<chunkcount; ++index)
{
delete pChunkList[index];
}
//free(pChunkList);
delete[] pChunkList;
}
void* Malloc(size_t size)
{
if(size>MAX_SIZE){
return malloc(size);
}
int index=0;
for(size_t tsize=MIN_SIZE; tsize<=MAX_SIZE; tsize*=2){
if(tsize>=size)break;
++index;
}
return pChunkList[index]->malloc();
}
void Free(void* pMem)
{
if(!free(pMem))MemoryChunk::free(pMem);
}
protected:
void* malloc(size_t size)
{
MemoryHeap* pHeap=(MemoryHeap*)::malloc(sizeof(HeapHeader)+size);
if(pHeap){
pHeap->header.size=size;
return &pHeap->pBuffer;
}
return NULL;
}
bool free(void* pMem)
{
MemoryHeap* pHeap=(MemoryHeap*)((char*)pMem-sizeof(HeapHeader));
if(pHeap->header.size>MAX_SIZE){
::free(pHeap);
return true;
}
return false;
}
private:
MemoryChunk** pChunkList;
int chunkcount;
};
#endif
ObejctManager.h文件,用于实现对象的创建与管理,比较简易。
#ifndef OBJECT_MANAGER_H
#define OBJECT_MANAGER_H
#include "StaticMemory.h"
/** @class ObjectManager
 * 实现利用内存池创建对象
 * 要求对象具有缺省构造函数
 */
template<typename T>
class ObjectManager
{
public:
typedef T ObjectType;
static ObjectType* Create(StaticMemory* pool)
{
void* pobject=pool->Malloc(sizeof(T));
new(pobject) ObjectType();
return static_cast<ObjectType*>(pobject);
}
static void Delete(StaticMemory* pool, ObjectType* pobject)
{
pobject->~ObjectType();
pool->Free(pobject);
}
};
#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

昵    称:
验证码:

相关文档:

  • 50条大牛C++编程开发学习建议
    每个从事C++开发的朋友相信都能给后来者一些建议,但是真正为此进行大致总结的很少。本文就给出了网上流传的对C++编程开发学习的50...
  • C语言常用功能库:Klib
    Klib 是一个C语言常用功能库,是一个轻量级和独立的 Glib 版本。大多数组件都是独立的外部库,除了标准C库,并且相互独立。要使用这...
  • 一张图总结Google C++编程规范(Google C++ Style Guide)
    Google C++ Style Guide是一份不错的C++编码指南,我制作了一张比较全面的说明图,可以在短时间内快速掌握规范的重点内容。不过规范毕...
  • 用C语言编写的微小HTTP解析器:PicoHTTPParser
    PicoHTTPParser是一个很小的,原始的,快速的HTTP请求/响应解析器。...
  • 轻量级的C++插件框架 - X3 C++ PluginFramework
    X3 C++ PluginFramework 代号为X3的C++轻量级通用插件框架平台是一套通用的C++轻量级插件体系,没有使用MFC、ATL、COM。可在Windows和Linux下编译...
  • C++语言的15个晦涩特性
    这个列表收集了 C++ 语言的一些晦涩(Obscure)特性,是我经年累月研究这门语言的各个方面收集起来的。C++非常庞大,我总是能学到一些...
  • C++的Twitter开发包 twitlib
    twitlib 是一个 C++ 的 Twitter 客户端开发包,现已改名为 QTwitLib。...
  • C/C++ 依赖管理器:biicode
    biicode 是一个支持多平台的 C/C++ 依赖管理器。可集成到 Visual Studio 和 Eclipse CDT 中。该工具目前是免费的,即将开源。...
  • 一个 C++ 的基准测试管理库:Celero
    对代码进行持续性开发和有意义的基准测试是一个复杂的任务。虽然测试工具本身(Intel® VTune™ Amplifier, SmartBear AQTime, Valgrind)与应用程...
  • C++11 多线程:数据保护
    在编写多线程程序时,多个线程同时访问某个共享资源,会导致同步的问题,这篇文章中我们将介绍 C++11 多线程编程中的数据保护。...
  • 将二进制文件加到C源码中的工具包:incbin.bat
    有时你需要将二进制数据嵌入至C / C++程序。 INCBIN是一个跨平台的工具,刚好用来做到这一点。不像其他的解决方案,INCBIN是使用操作系...
  • C++ RAD平台 U++
    U++是一个开源的跨平台C++ RAD平台,作者从98年就开始开发,到今年持续了十一年了。U++一直都是一个非常非常小众的开发平台,几乎没什...
  • C语言多平台程序库 LibU
    LibU 是一个支持多平台的 C 语言程序库,包含很多独立的模块,例如:内存分配、网络和URI解析、字符串处理、调试和日志记录,LibU 是...
  • C++类型安全SQL模版库:sqlpp11
    sqlpp11 是 C++ 的类型安全 SQL 模版库。...
  • C 语言编写的小型虚拟机:CarpVM
    CarpVM 是一个用 C 语言编写的小型虚拟机。可以嵌入在应用程序中使用。...
  • C++轻量级通用插件框架平台 X3C
    X3 C++ PluginFramework 代号为X3的C++轻量级通用插件框架平台是一套通用的C++轻量级插件体系,没有使用MFC、ATL、COM。可在Windows和Linux下编译...
  • CC++调试技巧
    在调试代码时,有时会遇到报错的的函数是没问题,而是调用它的函数传入的参数是异常的,而又没法通过代码准确定位到具体调用的位...
  • C/C++内存分布
    一个由C/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等...
  • 开发实时控制软件的C++框架 Orocos
    Orocos (Open Robot Control Software ) 是一个用来构建实时控制软件的C++框架,适合开发机器人或者机器的控制软件。...
  • 揭秘Facebook官方底层C++函数 - Folly
    Facebook近日公布了其官方底层C++函数Folly,Folly(该缩略语表示Facebook开源代码库)其实是C++11组件库,这些组件在设计时着眼于实用性和...