1、内存池简介
内存池是池化技术中的一种形式。通常我们在编写程序的时候回使用 new delete 这些关键字来向操作系统申请内存,而这样造成的后果就是每次申请内存和释放内存的时候,都需要和操作系统的系统调用打交道,从堆中分配所需的内存。如果这样的操作太过频繁,就会找成大量的内存碎片进而降低内存的分配性能,甚至出现内存分配失败的情况。而内存池就是为了解决这个问题而产生的一种技术。从内存分配的概念上看,内存申请无非就是向内存分配方索要一个指针,当向操作系统申请内存时,操作系统需要进行复杂的内存管理调度之后,才能正确的分配出一个相应的指针。而这个分配的过程中,我们还面临着分配失败的风险。所以,每一次进行内存分配,就会消耗一次分配内存的时间,设这个时间为 T,那么进行 n 次分配总共消耗的时间就是 nT;如果我们一开始就确定好我们可能需要多少内存,那么在最初的时候就分配好这样的一块内存区域,当我们需要内存的时候,直接从这块已经分配好的内存中使用即可,那么总共需要的分配时间仅仅只有 T。当 n 越大时,节约的时间就越多。
如上面两张图,一个内存池由若干个内存块组成,一个内存块由若干个内存单元组成(即链表节点)。
2、内存池的代码实现
内存池类MemoryPool的声明如下:
//MemoryPool.h
#pragma once
#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; //指向下一个内存块的指针
int nFree; //该内存块空闲节点的数量
FreeNode data[NumofObjects];//内存节点数组
FreeNode* freeNodeHeader; //空闲节点头指针
};
public:
MemPool()
{
freeNodeHeader = NULL;
memBlockHeader = NULL;
}
~MemPool()
{
MemBlock* ptr;
while (memBlockHeader) //删除所有的空闲块指针
{
ptr = memBlockHeader->pNext;
delete memBlockHeader;
memBlockHeader = ptr;
}
}
void* malloc(); //向内存池申请内存
void free(void*); //从内存池释放内存
private:
MemBlock* memBlockHeader; //空闲内存块头指针
};
内存池类MemoryPool的实现如下:
//MemoryPool.cpp
#include"MemoryPool.h"
//分配空闲的节点
template<int ObjectSize, int NumofObjects>
void* MemPool<ObjectSize, NumofObjects>::malloc()
{
void* freeNode;
if (!memBlockHeader)
{//内存池当前内存块链表为空,说明是第一次内存申请
MemBlock* newBlock = new MemBlock; //分配新的内存块,即分配20个内存单元
newBlock->pNext = NULL; //无下一个空闲块
newBlock->nFree = NumofObjects; //空闲节点数量
memBlockHeader = newBlock; //空闲块头指针
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;
}
MemBlock *myBlock = memBlockHeader; //当前内存块为内存块头链表指针指向的内存块
while (myBlock &&!myBlock->nFree) //寻找有空闲节点的内存块
myBlock = myBlock->pNext; //继续在下一个空闲块中查找
if (myBlock)
{//存在有空闲节点的内存块
memBlockHeader = myBlock; //更新空闲块头指针
}
else
{//当前不存在有空闲节点的内存块,需要再次申请分配内存
MemBlock* newBlock = new MemBlock; //分配新的内存块,即分配20个内存单元
newBlock->nFree = NumofObjects; //空闲节点数量
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;
//将新内存块加入到内存块链表
newBlock->pNext = memBlockHeader; //头插法
memBlockHeader = newBlock;
}
//返回空节点闲链表的第一个节点
void* freeNode = freeNodeHeader;
freeNodeHeader = freeNodeHeader->pNext;
memBlockHeader->nFree--; //当前内存块空闲内存节点减一
return freeNode;
}
//释放已经分配的节点
template<int ObjectSize, int NumofObjects>
void MemPool<ObjectSize, NumofObjects>::free(void* p)
{
MemoryBlock* pMyBlock = memBlockHeader;
bool bIsExist = false;
//判断p是否属于内存池
while (pMyBlock)
{
bIsExist = false;
for (int i = 0; i < NumofObjects; i++)
{
if (p == pMyBlock->newBlock->data[i])
{
bIsExist = true;
break;
}
}
if (bIsExist)
break;
pMyBlock = pMyBlock->pNext;
}
if (!bIsExist)//p不属于线程池
delete p; //将其回收给进程堆
else
{
FreeNode* pNode = (FreeNode*)p;
pNode->pNext = freeNodeHeader; //将释放的节点插入空闲节点头部
pNode->nFree--; //该内存块空闲节点数量减1
freeNodeHeader = pNode;
}
}
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