内存池:申请内存与释放内存

1、内存池简介
内存池是池化技术中的一种形式。通常我们在编写程序的时候回使用 new delete 这些关键字来向操作系统申请内存,而这样造成的后果就是每次申请内存和释放内存的时候,都需要和操作系统的系统调用打交道,从堆中分配所需的内存。如果这样的操作太过频繁,就会找成大量的内存碎片进而降低内存的分配性能,甚至出现内存分配失败的情况。而内存池就是为了解决这个问题而产生的一种技术。从内存分配的概念上看,内存申请无非就是向内存分配方索要一个指针,当向操作系统申请内存时,操作系统需要进行复杂的内存管理调度之后,才能正确的分配出一个相应的指针。而这个分配的过程中,我们还面临着分配失败的风险。所以,每一次进行内存分配,就会消耗一次分配内存的时间,设这个时间为 T,那么进行 n 次分配总共消耗的时间就是 nT;如果我们一开始就确定好我们可能需要多少内存,那么在最初的时候就分配好这样的一块内存区域,当我们需要内存的时候,直接从这块已经分配好的内存中使用即可,那么总共需要的分配时间仅仅只有 T。当 n 越大时,节约的时间就越多。
在这里插入图片描述
在这里插入图片描述
如上面两张图,一个内存池由若干个内存块组成,一个内存块由若干个内存单元组成(即链表节点)。

2、内存池的代码实现
内存池类MemoryPool的声明如下:

1//MemoryPool.h 2#pragma once 3#include <iostream> 4using namespace std; 5template<int ObjectSize, int NumofObjects = 20> 6class MemPool 7{ 8private: 9 //空闲节点结构体 10 struct FreeNode 11 { 12 FreeNode* pNext; //指向下一个空闲节点的指针 13 char data[ObjectSize]; //节点内容 14 }; 15 //内存块结构体 16 struct MemBlock 17 { 18 MemBlock* pNext; //指向下一个内存块的指针 19 int nFree; //该内存块空闲节点的数量 20 FreeNode data[NumofObjects];//内存节点数组 21 FreeNode* freeNodeHeader; //空闲节点头指针 22 }; 23public: 24 MemPool() 25 { 26 freeNodeHeader = NULL; 27 memBlockHeader = NULL; 28 } 29 ~MemPool() 30 { 31 MemBlock* ptr; 32 while (memBlockHeader) //删除所有的空闲块指针 33 { 34 ptr = memBlockHeader->pNext; 35 delete memBlockHeader; 36 memBlockHeader = ptr; 37 } 38 } 39 void* malloc(); //向内存池申请内存 40 void free(void*); //从内存池释放内存 41private: 42 MemBlock* memBlockHeader; //空闲内存块头指针 43}; 44 45

内存池类MemoryPool的实现如下:

1//MemoryPool.cpp 2#include"MemoryPool.h" 3//分配空闲的节点 4template<int ObjectSize, int NumofObjects> 5void* MemPool<ObjectSize, NumofObjects>::malloc() 6{ 7 void* freeNode; 8 if (!memBlockHeader) 9 {//内存池当前内存块链表为空,说明是第一次内存申请 10 MemBlock* newBlock = new MemBlock; //分配新的内存块,即分配20个内存单元 11 newBlock->pNext = NULL; //无下一个空闲块 12 newBlock->nFree = NumofObjects; //空闲节点数量 13 memBlockHeader = newBlock; //空闲块头指针 14 freeNodeHeader= &newBlock->data[0]; //设置内存块的第一个节点为空闲节点链表的首节点 15 //将内存块的其它节点串起来(初始化内存块) 16 for (int i = 1; i < NumofObjects; ++i) 17 { 18 newBlock->data[i - 1].pNext = &newBlock->data[i]; 19 } 20 newBlock->data[NumofObjects - 1].pNext = NULL; 21 } 22 MemBlock *myBlock = memBlockHeader; //当前内存块为内存块头链表指针指向的内存块 23 while (myBlock &&!myBlock->nFree) //寻找有空闲节点的内存块 24 myBlock = myBlock->pNext; //继续在下一个空闲块中查找 25 if (myBlock) 26 {//存在有空闲节点的内存块 27 memBlockHeader = myBlock; //更新空闲块头指针 28 } 29 else 30 {//当前不存在有空闲节点的内存块,需要再次申请分配内存 31 MemBlock* newBlock = new MemBlock; //分配新的内存块,即分配20个内存单元 32 newBlock->nFree = NumofObjects; //空闲节点数量 33 freeNodeHeader = &newBlock->data[0]; //设置内存块的第一个节点为空闲节点链表的首节点 34 //将内存块的其它节点串起来(初始化内存块) 35 for (int i = 1; i < NumofObjects; ++i) 36 { 37 newBlock->data[i - 1].pNext = &newBlock->data[i]; 38 } 39 newBlock->data[NumofObjects - 1].pNext = NULL; 40 //将新内存块加入到内存块链表 41 newBlock->pNext = memBlockHeader; //头插法 42 memBlockHeader = newBlock; 43 } 44 //返回空节点闲链表的第一个节点 45 void* freeNode = freeNodeHeader; 46 freeNodeHeader = freeNodeHeader->pNext; 47 memBlockHeader->nFree--; //当前内存块空闲内存节点减一 48 return freeNode; 49} 50//释放已经分配的节点 51template<int ObjectSize, int NumofObjects> 52void MemPool<ObjectSize, NumofObjects>::free(void* p) 53{ 54 MemoryBlock* pMyBlock = memBlockHeader; 55 bool bIsExist = false; 56 //判断p是否属于内存池 57 while (pMyBlock) 58 { 59 bIsExist = false; 60 for (int i = 0; i < NumofObjects; i++) 61 { 62 if (p == pMyBlock->newBlock->data[i]) 63 { 64 bIsExist = true; 65 break; 66 } 67 } 68 if (bIsExist) 69 break; 70 pMyBlock = pMyBlock->pNext; 71 } 72 if (!bIsExist)//p不属于线程池 73 delete p; //将其回收给进程堆 74 else 75 { 76 FreeNode* pNode = (FreeNode*)p; 77 pNode->pNext = freeNodeHeader; //将释放的节点插入空闲节点头部 78 pNode->nFree--; //该内存块空闲节点数量减1 79 freeNodeHeader = pNode; 80 } 81} 82 83

3、参考资源
C++ 应用程序性能优化,第 6 章:内存池:
https://www.ibm.com/developerworks/cn/linux/l-cn-ppp/index6.html
C++ 内存池介绍与经典内存池的实现:
https://blog.csdn.net/K346K346/article/details/49538975?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

代码交流 2021