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

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

猜你喜欢

转载自blog.csdn.net/fazstyle/article/details/106863532
今日推荐