最简单的内存池-原理与实现
??? 内存池的主要作用,简单地说来,便是提高内存的使用效率。堆内存的申请与释放(new/delete及malloc/free),涉及复杂的内存分配算法, 相比由简单CPU指令支持的栈内存的申请与释放,则是慢上了数量级。另一方面,栈的大小是有限制的,在需要大量内存的操作时,堆的使用是必要的。当然,频繁地申请与释放堆内存,效率是很低的,这也就是内存池出现的原因。
??? 类似std::vector容器,在向vector里添加一个元素之前,该容器往往提前申请了一大块内存,实际添加时就只需要拿出其中一小块来使用,不用每添加一个都使用new一次。内存池所做的,也就是一次申请一块大内存,然后再小块小块地拿出来用:一次申请NM大小的内存,必然比N次申请M大小的内存要快,这就是内存池高效的简单解释。
??? 一般来说,内存池可以按两种维度划分:单线程与多线程,可变大小和固定大小。其中最简单的单线程固定大小的内存池。这类内存池只考虑单线程的程序,分配与回收的对象都是固定大小的。这个简单的内存池类模板声明如下:
?
??? 当用户向内存池申请一个对象T时,内存池会检查freeChunk是否为NULL。不为NULL时,表示还有自由的小内存块(ObjectChunk)可供使用,将它返回给用户,并将freeChunk移到下一个自由内存块;若freeChunk为NULL,则内存池需要使用 new/malloc从申请一块大内存(MemBlock),然后将其中的BaseSize个ObjectChunk都串连上形成链表,再将 freeChunk做为头指针。这时freeChunk不为NULL了,可以提供给用户一块小内存了(具体即是,取出链表的首结点)。template<typename T, int BaseSize=32>class SimpleMemPool{private:#pragma pack(push, 1) union ObjectChunk { ObjectChunk * next; char buf[ sizeof(T)]; }; struct MemBlock { MemBlock* next; ObjectChunk chunks[BaseSize]; };#pragma pack(pop) SimpleMemPool(const SimpleMemPool<T, BaseSize> &) { } MemBlock * head; ObjectChunk * freeChunk;public: SimpleMemPool() : head(0), freeChunk(0) { } ~SimpleMemPool() { while(head) { MemBlock * t = head; head = head->next; delete t; } } T* New() { if (!freeChunk) { MemBlock * t = new MemBlock; t->next = head; head = t; for(unsigned int i=0; i<BaseSize-1;++i) { head->chunks[i].next = & (head->chunks[i+1]); } head->chunks[BaseSize-1].next = 0; freeChunk = & (head->chunks[0]); } ObjectChunk * t = freeChunk; freeChunk = freeChunk->next; return reinterpret_cast<T*>(t); } void Delete(T* t) { ObjectChunk * chunk = reinterpret_cast<ObjectChunk*>(t); chunk->next= freeChunk; freeChunk = chunk; }};?