c++内存池
参考文章:C++ 内存池介绍与经典内存池的实现-CSDN博客
为了解决频繁使用new、malloc等函数造成的内存碎片问题。
1. 内存池的分类
从线程安全的角度来分,可分为单线程线程池和多线程线程池。单线程线程池整个生命周期被一个线程使用,不用考虑互斥访问的问题,多线程内存池可能被多个线程共享,每次分配释放内存时需要加锁。
就可分配内存单元大小来分,可分为固定内存池和可变内存池。固定内存池是指每次分配的内存单元大小是固定不变的;可变内存池每次分配的内存单元大小是可变的,效率相对于固定内存池会低一些。
2. 内存池技术
2.1 经典内存池
用于分配大量大小相同的对象,分配释放速度快。
实现过程:
1)先申请一块连续的内存空间,该段内存空间能够容纳一定数量的对象;
2)每个对象连同一个指向下一个对象的指针一起构成一个内存节点(Memory Node)。各个空闲的内存节点通过指针形成一个链表,链表的每一个内存节点都是一块可供分配的内存空间;
3)某个内存节点一旦分配出去,从空闲内存节点链表中去除;
4)一旦释放了某个内存节点的空间,又将该节点重新加入空闲内存节点链表(头插);
5)如果一个内存块的所有内存节点分配完毕,若程序继续申请新的对象空间,则会再次申请一个内存块来容纳新的对象。新申请的内存块会加入内存块链表中。

struct FreeNode { FreeNode* pNext; char data[ObjectSize]; };
struct MemBlock { MemBlock *pNext; FreeNode data[NumofObjects]; };
#include <iostream> using namespace std; template<int ObjectSize, int NumofObjects = 20> class MemPool { private: //空闲节点结构体 struct FreeNode{ FreeNode* pNext; char data[ObjectSize]; }; //内存块结构体 struct MemBlock{ MemBlock* pNext; FreeNode data[NumofObjects]; }; FreeNode* freeNodeHeader; MemBlock* memBlockHeader; public: MemPool() { freeNodeHeader = NULL; memBlockHeader = NULL; } ~MemPool() { MemBlock* ptr; while (memBlockHeader) { ptr = memBlockHeader->pNext; delete memBlockHeader; memBlockHeader = ptr; } } void* malloc(); void free(void*); }; // 分配空闲的结点。 template<int ObjectSize, int NumofObjects> void* MemPool<ObjectSize, NumofObjects>::malloc(){ //无空闲节点,申请新内存块 if (freeNodeHeader == NULL){ MemBlock* newBlock = new MemBlock; newBlock->pNext = NULL; freeNodeHeader=&newBlock->data[0]; //设置内存块的第一个节点为空闲节点链表的首节点 //将内存块的其它节点串起来 for (int i = 1; i < NumofObjects; ++i) { newBlock->data[i - 1].pNext = &newBlock->data[i]; } newBlock->data[NumofObjects - 1].pNext=NULL; // 首次申请内存块 if (memBlockHeader == NULL) { memBlockHeader = newBlock; } else { // 将新内存块加入到内存块链表。 newBlock->pNext = memBlockHeader; memBlockHeader = newBlock; } } // 返回空节点闲链表的第一个节点。 void* freeNode = freeNodeHeader; freeNodeHeader = freeNodeHeader->pNext; return freeNode; } // 释放已经分配的结点。 template<int ObjectSize, int NumofObjects> void MemPool<ObjectSize, NumofObjects>::free(void* p) { FreeNode* pNode = (FreeNode*)p; pNode->pNext = freeNodeHeader; //将释放的节点插入空闲节点头部 freeNodeHeader = pNode; } class ActualClass { static int count; int No; public: ActualClass() { No = count; count++; } void print() { cout << this << ": "; cout << "the " << No << "th object" << endl; } void* operator new(size_t size); void operator delete(void* p); }; // 定义内存池对象 MemPool<sizeof(ActualClass), 2> mp; void* ActualClass::operator new(size_t size) { return mp.malloc(); } void ActualClass::operator delete(void* p) { mp.free(p); } int ActualClass::count = 0; int main() { ActualClass* p1 = new ActualClass; p1->print(); ActualClass* p2 = new ActualClass; p2->print(); delete p1; p1 = new ActualClass; p1->print(); ActualClass* p3 = new ActualClass; p3->print(); delete p1; delete p2; delete p3; }
主要开销在没有空闲节点,申请新的内存块时,需要把内存块内部节点初始化。
正常情况下申请和分配的复杂度都是o(1)。
有以下缺点:1. 不能申请对象数组;2. 内存块个数只能增大不能减少;3. 没有考虑多线程安全。
待续。。。

浙公网安备 33010602011771号