c++单线程内存池

来源:互联网 发布:php能做幻灯片吗 编辑:程序博客网 时间:2024/05/01 13:55

对于一般直接 new 与delete 性能较差,可以自己管理写内存的申请与释放。

版本0:

复制代码
class Rational{public:    Rational(int a=0, int b =1 ): n(a),d(b){}private:    int n;    int d;};
复制代码

 

版本1: 专用的内存管理器

这版本是通过重载 目标类 中的new 与delete 实现内存管理,只适用于目标类,但是速度是最快的。

实现方式是维护一个链表,类中静态声明链表头,链表维护一串空间,通过类型转换在  目标类 和 链表指针 之间转换。

如果内存不够(freelist=NULL)是一次申请较多内存进行维护。

链表的添加删除(对于函数释放空间与申请空间)是在链首操作。

书上47 行 52行通过 静态类型转换不成功,改为了 强制转换,在codeblock g++ 中能够编译运行。

主要在主函数中调用静态函数 链表的申请与释放。即74 86行。

注意65 行把原来的delete [] 改为了 delete[] (char*)  原因可以参考第二版本。

复制代码
 1 class NextOnFreeList 2 { 3 public: 4     NextOnFreeList *next; 5 }; 6  7 class Rational 8 { 9 public:10     Rational(int a=0, int b =1 ): n(a),d(b){}11     inline void *operator new(size_t size);12     inline void operator delete(void * doomed, size_t size);13 14     static void newMemPool(){ expandTheFreeList();   }15     static void deleteMemPool();16     static void expandTheFreeList();17 private:18     int n;19     int d;20     static NextOnFreeList * freelist;21     enum{   EXPANSION_SIZE = 32 };22 };23 24 inline25 void * Rational::operator new( size_t size)26 {27     if(0 == freelist)28     {29         expandTheFreeList();30     }31     NextOnFreeList * head =freelist;32     freelist=head->next;33     return head;34 }35 36 inline37 void Rational::operator delete(void * doomed ,size_t size)38 {39     NextOnFreeList *head=static_cast<NextOnFreeList *>( doomed);40     head->next=freelist;41     freelist=head;42 }43 44 void Rational:: expandTheFreeList()45 {46     size_t size= (sizeof(Rational)>sizeof(NextOnFreeList*))?sizeof(Rational):sizeof(NextOnFreeList*);47 //    NextOnFreeList *runner= static_cast<NextOnFreeList *>(new char [size]);48     NextOnFreeList *runner= (NextOnFreeList *)(new char [size]);49     freelist=runner;50     for(int i=0;i<EXPANSION_SIZE;i++)51     {52 //        runner->next= static_cast<NextOnFreeList *>( new char [size] );53         runner->next= (NextOnFreeList *) new char [size] ;54         runner=runner->next;55     }56     runner->next= 0;57 }58 59 void Rational::deleteMemPool()60 {61     NextOnFreeList * nextPtr;62     for(nextPtr=freelist;nextPtr!=NULL;nextPtr=freelist)63     {64         freelist=freelist->next;65         delete [](char *)nextPtr;66     }67 }68 69 NextOnFreeList * Rational::freelist =0;70 71 int main()72 {73     Rational * array[1000];74     Rational::newMemPool();75     for(int j=0;j<500;j++)76     {77         for(int i=0;i<1000;i++)78         {79             array[i]=new Rational(i);80         }81         for(int i=0;i<1000;i++)82         {83             delete array[i];84         }85     }86     Rational::deleteMemPool();87     return 0;88 }
复制代码

 

版本2:固定大小对象的内存池

考虑版本1,可以通过模板实现管理特定的对象。最主要的参数是类的大小。

实现是专门声明一个管理内存的内存池类,使用模板实现,提供alloc() free() 接口,给类申请与释放内存。

内存池类的实现是自身有一个MemoryPool<T>* next 指针用来指向维护的链表,内存的操作都在链首位置。

第32行书上代码编译不通过,

delete [] nextPrt;

 时,nextPrt  的类型为 MemoryPool * ,这样在释放的时候再一次调用了 ~MemoryPool 而不是默认的内存释放。(第一次调用是上层类的析构函数调用的)

所以在这改为了 

delete [](char*)nextPrt;

这样才能成功释放内存。

下面的是底层类的代码。

复制代码
 1 template<class T> 2 class MemoryPool 3 { 4 public: 5     MemoryPool(size_t size =EXPANSION_SIZE); 6     ~MemoryPool(); 7  8     //从空闲列表中分配T元素 9     inline void * alloc(size_t size);10     inline void free(void * someElement);11 private:12     MemoryPool<T>* next;13     enum{EXPANSION_SIZE=2};14     //将空闲元素添加至空闲列表15     void expanTheFreeList(int howMany=EXPANSION_SIZE);16 };17 18 template<class T>19 MemoryPool<T>::MemoryPool(size_t size)20 {21     expanTheFreeList( size);22 }23 24 template<class T>25 MemoryPool<T>::~MemoryPool()26 {27     cout<<next<<endl;28     MemoryPool<T> * nextPrt=next;29     for(nextPrt=next;nextPrt!=NULL;nextPrt=next)30     {31         next=next->next;32 //        delete [] nextPrt;33         delete [](char*)nextPrt;34     }35 }36 37 template<class T>38 inline39 void * MemoryPool<T>::alloc(size_t size)40 {41     if(!next)42     {43         expanTheFreeList();44     }45     MemoryPool<T> * head=next;46     next=head->next;47     return head;48 }49 50 template<class T>51 inline52 void MemoryPool<T>::free(void * doomed)53 {54 //    MemoryPool<T> *head=static_cast<MemoryPool<T>* >(doomed);55     MemoryPool<T> *head=(MemoryPool<T>* )(doomed);56     head->next=next;57     next=head;58 }59 60 template<class T>61 void MemoryPool<T>::expanTheFreeList(int howmany)62 {63     size_t size=(sizeof(T)>sizeof(MemoryPool<T>*))? sizeof(T):sizeof(MemoryPool<T>*);64 //    MemoryPool<T>* runner =static_cast< MemoryPool<T>* >(new char [size]);65     MemoryPool<T>* runner =( MemoryPool<T>* )(new char [size]);66     next=runner;67     for(int i=0 ;i<howmany;i++)68     {69 //        runner->next=static_cast< MemoryPool<T>* >(new char [size]);70         runner->next=( MemoryPool<T>* )(new char [size]);71         runner=runner->next;72     }73     runner->next=0;74 }
复制代码

 

下面是调用类与使用的代码:

12行的时候会调用 MemoryPool 的构造函数。

复制代码
 1 //使用内存池的类 2 class Rational 3 { 4 private: 5     int n; 6     int d; 7     static MemoryPool<Rational > *memPool; 8 public: 9     Rational(int a=0, int b=1) : n(a),d(b){}10     void *operator new(size_t size){    return memPool->alloc(size);    }11     void operator delete(void* doomed, size_t size){    memPool->free(doomed);    }12     static void newMemPool(){   memPool=new MemoryPool <Rational >;    }13     static void deleteMemPool(){    delete memPool; }14 };15 16 MemoryPool<Rational>* Rational::memPool=0;17 18 int main()19 {20     //...21     Rational::newMemPool();22     //...23     Rational::deleteMemPool();24     //..25     return 0;26 }
复制代码

 

 

版本3:将版本2的 固定长度改为不定长度,称为可变大小内存池

首先要有个认识,既然是可变长度,那么通常释放后,再一次申请的时候不会申请这一段内存的,(通过代码的实现是不打算复用内存的)第一是不适用,第二就算够那么将造成管理的困难

想想申请而来123连续的3段内存,第二段内存需要释放,这大小就固定下来了(13还没释放),如果想复用,这个就另外实现吧。 - -!

实现方式是在内存池(版本2 的MemoryPool)下层在创建一个类,用来指向一个大内存块,一个计数,表示前多少内存已经使用了,如果不够就再一次申请一个内存块。至于浪费的考虑,看书上说的吧。 - -!

MemoryPool 还是第二版本的功能,提供接口,不过对于物理内存的申请就下放了。

仍然是维护链表实现,还是链首操作。

内存块 类:

因为最低成的释放free 基于设计因素什么也不赶,要想的也可以对free 修改,参数什么的都有传递。

29行书上是直接 delete [] mem;  不过这样会提示void* 操作,强制转换了

复制代码
 1 class MemoryChunk 2 { 3 private: 4     MemoryChunk* next;//指向下个内存块 5     void * mem;//指向可用的内存 6     size_t chunkSize;//该内存块的大小 7     size_t bytesAlreadyAllocated;//已经分配的字节数 8 public: 9     MemoryChunk(MemoryChunk *nextChunk ,size_t chunkSize);10     ~MemoryChunk();11 12     inline void* alloc(size_t size);13     inline void free (void* someElement);14     MemoryChunk* nextMemChunk() {  return next; }15     size_t spaceAvailable() {   return chunkSize - bytesAlreadyAllocated;   }16     enum{   DEFUALT_CHUNK_SIZE = 4096  };17 };18 19 MemoryChunk::MemoryChunk(MemoryChunk* nextChunk, size_t reqSize)20 {21     chunkSize = (reqSize > DEFUALT_CHUNK_SIZE) ? reqSize : DEFUALT_CHUNK_SIZE;22     next = nextChunk;23     bytesAlreadyAllocated=0;24     mem= new char [chunkSize];25 }26 27 MemoryChunk::~MemoryChunk()28 {29     delete [] (char*)mem;30 }31 32 void* MemoryChunk::alloc(size_t requestSize)33 {34     void* addr = (void* )((size_t )mem + bytesAlreadyAllocated );35     bytesAlreadyAllocated+=requestSize;36     return addr;37 }38 39 inline void MemoryChunk::free(void* someElement ) {}
复制代码

 

内存池类:

必须注意16行,对链表的末尾赋0,书上是没有的,这句没有会导致析构的时候不能判断结束。

第26行的时候是调用memChunk的析构函数的。

复制代码
 1 //内存池类 2 class ByteMemoryPool 3 { 4 private: 5     MemoryChunk* listOfMemoryChunks; 6     void expandStorage(size_t reqSize); 7 public: 8     ByteMemoryPool(size_t initSize = MemoryChunk::DEFUALT_CHUNK_SIZE); 9     ~ByteMemoryPool();10     inline void* alloc(size_t size);11     inline void free(void* someElement);12 };13 14 ByteMemoryPool::ByteMemoryPool(size_t initSize)15 {16     listOfMemoryChunks=NULL;17     expandStorage(initSize);18 }19 20 ByteMemoryPool::~ByteMemoryPool()21 {22     MemoryChunk* memChunk = listOfMemoryChunks;23     while(memChunk)24     {25         listOfMemoryChunks = memChunk->nextMemChunk();26         delete memChunk;27         memChunk = listOfMemoryChunks;28     }29 }30 31 inline32 void* ByteMemoryPool::alloc(size_t requestSize)33 {34     size_t space = listOfMemoryChunks->spaceAvailable();35     if(space<requestSize)36     {37         expandStorage(requestSize);38     }39     return listOfMemoryChunks->alloc(requestSize);40 }41 42 inline43 void ByteMemoryPool::free(void* doomed)44 {45     listOfMemoryChunks->free(doomed);46 }47 48 void ByteMemoryPool::expandStorage(size_t requestSize)49 {50     listOfMemoryChunks = new MemoryChunk(listOfMemoryChunks,requestSize);51 }
复制代码

 

调用类与测试:

21 、33对应创建内存池与释放内存池,是同一个类中共享内存池。

复制代码
 1 //调用类 2 class Rational 3 { 4 private: 5     int n; 6     int d; 7     static ByteMemoryPool* memPool; 8 public: 9     Rational(int a=0, int b=1):n(a),d(b){}10     void* operator new (size_t size){   return memPool->alloc(size);    }11     void operator delete (void* doomed, size_t size){   memPool->free(doomed);  }12     static void newMemPool(){   memPool = new ByteMemoryPool;   }13     static void deleteMemPool(){    delete memPool; }14 };15 16 ByteMemoryPool* Rational::memPool=0;17 18 int main()19 {20     Rational * array[1000];21     Rational::newMemPool();22     for(int j=0;j<500;j++)23     {24         for(int i=0;i<1000;i++)25         {26             array[i]=new Rational(i);27         }28         for(int i=0;i<1000;i++)29         {30             delete array[i];31         }32     }33     Rational::deleteMemPool();34     return 0;35 }
复制代码

 

 总结:

1.灵活性以速度为代价。

2.全局内存管理器是通用的(new 和 delete),因此代价高。

3.专用内存管理器比全局内存管理器快一个数量级以上。

4.如果主要分配固定大小的内存块,专用的固定大小内存管理器将明显地提升性能。

5.如果主要分配限于单线程内存块,那么内存块管理器也会有类似的性能提高。

 

0 0
原创粉丝点击