c内存池设计学位论文.docx
《c内存池设计学位论文.docx》由会员分享,可在线阅读,更多相关《c内存池设计学位论文.docx(23页珍藏版)》请在冰豆网上搜索。
c内存池设计学位论文
C++内存池设计
在项目中进程要对变量和对象分配空间,由于频繁的使用new和delete很消耗程序的运行时间,而且容易产生各种内存泄露,内存释放错误等问题。
为此,需要设计一个通用的内存池来完成相关的分配和释放的工作。
建立内存池:
首先向系统申请一块内存,这块内存的大小由使用者根据需要设置初始内存大小。
定义一个如下面代码所示的双向链表,将从系统分配的内存分为若干块。
使用双向链表方便指针向前和向后遍历查找。
链表中*Data指向了系统分配的内存,pUser使用二级指针保存了内存申请者的地址,方便以后系统内存块更改,改变申请者的指向。
后面会详细介绍。
将双向链表指向指向内存如下所示:
假设内存池初始块数为4块,每块的大小为100个字节,则向系统申请400个字节的内存块,每块的大小为100字节。
之后使用双向链表DATA指针指向内存块,每个指针能分配的大小如图所示从大到小递减。
对象内存分配:
对内存的链表指针分配好后,用户可以使用内存池进行内存分配,对于用户的内存分配有两种情况,一种是在现有的内存池中能找到合适的内存块,另一种情况是现有内的内存池没有足够的内存块来分配,需要重新向系统申请内存来满足用户的需求。
下面分别就这两种内存分配情况进行说明:
情况1内存池有足够的内存块进行分配
假设用户申请了240个字节的内存空间,内存池现在有四个内存块空闲,每个内存块的大小为100字节,那么内存池将会给用户取整分配三个内存块。
如上图所示,并将指向400内存块的指针的DATE返回给用户使用。
情况2内存池没有足够的内存块进行分配
接着上图,假设用户现在要接着分别300字节的内存空间,现有内存池的大小已经不能满足,因此需要扩大现有的内存池使用大小。
考虑到由于分配给用户的内存空间必须要是连续的内存块,因此这个连续的内存块越大,能分配给用户的内存就多。
因此使用C语言的realloc函数来满足要求。
函数简介
原型:
externvoid*realloc(void*mem_address,unsignedintnewsize);
语法:
指针名=(数据类型*)realloc(要改变内存大小的指针名,新的大小)。
//新的大小一定要大于原来的大小,不然的话会导致数据丢失!
头文件:
#include有些编译器需要#include,在TC2.0中可以使用alloc.h头文件
功能:
先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:
原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。
即重新分配存储器块的地址。
返回值:
如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。
由realloc函数定义可知,新分配的内存空间可能是在原有的内存基础上扩充,还有可能是在另外的一个地方新开辟一块内存。
无论哪种情况多要对新加的内存进行指针指向分配。
并且对于第二种情况,会出现的问题是原有的指针全都失效。
因为原有指向的内存已经不存在了,因此指向它的指针将失效,原有分配的对象也将失效。
为了解决这个问题,在新分配内存后需要重定向原有的指针,并且使用二级指针改变已经分配了对象的地址的指向,使它指向新内存。
重定向原有内存的指针的指向,和已经分配了内存的对象的指向。
对象释放内存:
假如先前的申请了250个字节分配了三个内存块的用户释放了内存,这时链表指针向后查找直到找到第一个被使用的内存块,或链表结尾。
之后在先前查找直到找到前面第一个被使用的内存块或者是头指针,之后更新这个区间段内存块的大小。
释放内存池:
首先释放向系统申请的内存块,之后在清空所有的双向链表。
释放向系统申请的内存
释放双向链表。
后续改进:
1,需要对多线程的支持,目前的内存池还只能在单线程的环境下运行。
2,如果之前得到内存的对象,在新内存分配前有指针复制操作,原有对象可以通过保存的指针地址进行重定向,但是之前分别的对象不能保证。
引进对于分配的对象尽量不要使用指针复制。
如果一定需要这么做,那就在每次使用前,在重定向一下。
重新进行一次复制操作(保险起见,不知道我的表述是否清楚明白)。
源代码:
头文件链表节点的定义
#include
#include
#include
#include
usingnamespacestd;
namespaceMemePool
{
typedefunsignedcharEigthByte;
//内存池的默认大小和分配节点的默认大小
staticconstsize_tDEFAULTMEMEPOOLSIZE=1000;
staticconstsize_tDEFAULTMEMENODESIZE=100;
//内存初始分配内容二进制位11111111
staticconstintNEW_ALLOCATED_MEMORY_CONTENT=0xFF;
//内存分配节点(双向链表)
typedefstructTMemeNode
{
//指向前一节点
TMemeNode*first;
//指向后一节点
TMemeNode*next;
//节点大小
size_tidataSize;
//节点是否被使用
boolisUsed;
//分配的节点的后一节点
TMemeNode*pEndNode;
//记录内存池分配的首地址
boolisMemeBegin;
//保存分配的内存地址
EigthByte*Data;
//使用者对象的地址
void**pUser;
}TMemeLinkNode;
};
内存池实现:
namespaceMemePool
{
//内存池的实现作者邮箱a584851044@
classCMemePool
{
public:
//线程池构造函数
CMemePool(constsize_t&sInitialMemoryPoolSize=DEFAULTMEMEPOOLSIZE,
constsize_t&sMemoryChunkSize=DEFAULTMEMENODESIZE);
~CMemePool();
//分配内存
void*GetMemeroy(void**p,constsize_t&sSize);
//释放分配内存
voidFreeAllocMemeroy(void*p,constsize_t&sSize);
//释放内存池所有内存
voidFreeAllMemeroy();
//展示内存池的使用情况
voidShowTheMemePoolStatue();
//获取错误信息
voidGetErrorInfo();
private:
//禁止复制与构造,要传递就用引用吧
CMemePool(CMemePool*tCMemePool);
CMemePool&operator=(CMemePool&tCMemePool);
voidAllocPoolMemeroy();
voidCalLinkNodeNum();
voidCalMemeSize();
voidLinkMemeryToNode(EigthByte*PAlloc);
voidUpdateLinkNodeSize(TMemeNode*PNode);
voidCalNeetLinkNumber(constsize_t&sSize);
void*FindMemeNode(constsize_t&sSize);
TMemeNode*SearchAllocNode(void*p);
voidCleanAllMemeDate();
voidCleatAllLinkNode();
voidResetLinkToMemery();
//双向链表的头节点
TMemeLinkNode*m_Head;
//双向链表的当前节点
TMemeLinkNode*m_Current;
//双向链表的最后节点
TMemeLinkNode*m_LastNode;
EigthByte*m_PAlloc;
//保存第一次运行头地址
boolm_isFirst;
//内存块分配数目
size_tm_Number;
//内存块总的数目
size_tm_AllNumber;
//每一个内存块的大小
size_tm_MemLinkSize;
//内存池分配的大小
size_tm_MemePollSize;
//内存块总分配大小
size_tm_AllAloctsize;
//内存池使用的大小
size_tm_MemePoolUseSize;
//内存池空闲的大小
size_tm_MemePoolFreeSize;
//分配了多少个对象
size_tm_iUseObject;
//保存错误信息
stringm_sError;
//保存请求分配内存用户信息
void**m_User;
};
};
//---------------------------------------------------------------------------
//recalloc分配新内存后,之前指向旧内存的指针就失效了
//需要重新定位,之前分配对象的指向也要重新定位
namespaceMemePool
{
/*****************************************
内存池构造函数
by风清扬song13-07-28
*****************************************/
CMemePool:
:
CMemePool(constsize_t&sInitialMemoryPoolSize,constsize_t&sMemoryChunkSize)
{
//初始化内存池的大小
m_MemePollSize=sInitialMemoryPoolSize;
//初始化每个内存块的大小
m_MemLinkSize=sMemoryChunkSize;
//初始化一些参数
m_MemePoolFreeSize=0;
m_MemePoolUseSize=0;
m_Current=NULL;
m_LastNode=NULL;
m_Number=0;
m_AllAloctsize=0;
m_AllNumber=0;
m_iUseObject=0;
m_Head=newTMemeLinkNode;
m_Head->next=NULL;
m_Head->first=NULL;
m_Head->Data=NULL;
m_isFirst=true;
//分配线程池函数
AllocPoolMemeroy();
}
/*****************************************
内存池析构函数
by风清扬song13-07-28
*****************************************/
CMemePool:
:
~CMemePool()
{
FreeAllMemeroy();
}
/*****************************************
内存池分配内存函数
by风清扬song13-07-28
*****************************************/
voidCMemePool:
:
AllocPoolMemeroy()
{
//计算需要的链表节点数目
CalLinkNodeNum();
//计算真正要分配的内存大小
CalMemeSize();
m_AllNumber=m_AllNumber+m_Number;
m_AllAloctsize+=m_MemePollSize;
m_MemePoolFreeSize+=m_MemePollSize;
//追加分配内存,原有内存的内容不会受到影响
m_PAlloc=(EigthByte*)realloc(m_PAlloc,(m_AllAloctsize)*sizeof(EigthByte));
//内存分配失败
if(NULL==m_PAlloc)
{
m_sError="AllocMemeroyPoolFailture";
return;
}
//不是第一次分配内存
if(false==m_isFirst)
{//新分配内存后原有指针全失效,需要重定向
ResetLinkToMemery();
}
//分配的内存内容初始化
//memset(((void*)PAlloc),NEW_ALLOCATED_MEMORY_CONTENT,m_MemePollSize);
//将分配的线程池内存与链表节点关联
LinkMemeryToNode(&m_PAlloc[m_AllAloctsize-m_MemePollSize]);
}
/*****************************************
将原内存的指针进行重定向(Alloc后原有内存可能被释放了)
by风清扬song13-07-28
*****************************************/
voidCMemePool:
:
ResetLinkToMemery()
{
TMemeLinkNode*pTemp=m_Head->next;
intiIndex=0;
while(NULL!
=pTemp)
{
//重定向指针链表的指向
pTemp->Data=&m_PAlloc[iIndex*m_MemLinkSize];
if(NULL!
=pTemp->pUser)
{
//重定向用户指针的指向
*pTemp->pUser=pTemp->Data;
}
iIndex++;
pTemp=pTemp->next;
}
}
/*****************************************
计算需要的内存链表节点数目
by风清扬song13-07-28
*****************************************/
voidCMemePool:
:
CalLinkNodeNum()
{
floatfTempValue=m_MemePollSize/m_MemLinkSize;
//向上取整需要的节点数目
m_Number=ceil(fTempValue);
}
/*****************************************
计算内存池真正分配的内存的大小
by风清扬song13-07-28
*****************************************/
voidCMemePool:
:
CalMemeSize()
{
m_MemePollSize=(size_t)(m_Number*m_MemLinkSize);
}
/*****************************************
将分配的内存和链表节点相关联
by风清扬song13-07-28
*****************************************/
voidCMemePool:
:
LinkMemeryToNode(EigthByte*PAlloc)
{
TMemeLinkNode*PNode;
//遍历每一个节点分配空间
for(size_tiIndex=0;iIndex{
PNode=newTMemeLinkNode();
if(NULL==m_LastNode)
{
PNode->next=m_Head->next;
m_Head->next=PNode;
PNode->first=m_Head;
m_LastNode=PNode;
}
else
{
PNode->next=m_LastNode->next;
m_LastNode->next=PNode;
PNode->first=m_LastNode;
m_LastNode=PNode;
}
m_LastNode->isUsed=false;
m_LastNode->idataSize=m_MemePollSize-iIndex*m_MemLinkSize;
m_LastNode->Data=&PAlloc[iIndex*m_MemLinkSize];
m_LastNode->isMemeBegin=false;
m_LastNode->pUser=NULL;
//记录内存块的首地址,释放时使用
if(true==m_isFirst&&0==iIndex)
{
m_LastNode->isMemeBegin=true;
m_isFirst=false;
}
}
UpdateLinkNodeSize(m_LastNode);
}
/*****************************************
更新当前节点的前后大小值
by风清扬song13-07-28
*****************************************/
voidCMemePool:
:
UpdateLinkNodeSize(TMemeNode*PNode)
{
TMemeNode*PTemp;
PTemp=PNode->next;
intiDateSize=0;
//当前节点的后一个节点没分配,得到它的DataSize值
if(NULL!
=PTemp&&false==PTemp->isUsed)
{
iDateSize=PTemp->idataSize;
}
//由最后一个节点在向前遍历,更新所有的节点大小值
intiIndex=1;
while(PNode!
=m_Head&&false==PNode->isUsed)
{
PNode->idataSize=iIndex*m_MemLinkSize+iDateSize;
iIndex++;
PNode=PNode->first;
}
m_Current=PNode->next;
}
/*****************************************
分配内存空间
by风清扬song13-07-28
*****************************************/
void*CMemePool:
:
GetMemeroy(void**p,constsize_t&sSize)
{
m_MemePoolFreeSize-=sSize;
m_MemePoolUseSize+=sSize;
//保存请求内存分配的用户信息
m_User=p;
//增加分配对象数目
m_iUseObject++;
//有合适的内存块
void*pFind=FindMemeNode(sSize);
if(NULL!
=pFind)
{
returnpFind;
}
TMemeNode*PTemp;
PTemp=m_Current;
m_Current=m_Current->next;
//遍历内存块找到合适的内存
while(PTemp!
=m_Current)
{
//走到结尾,从头来
if(NULL==m_Current)
{
m_Current=m_Head->next;
}
//跳过已经分配的节点
if(true==m_Current->isUsed)
{
m_Current=m_Current->pEndNode;
//m_Current=m_Current->first;
}
pFind=FindMemeNode(sSize);
if(NULL!
=pFind)
{
returnpFind;
}
if(PTemp==m_Current)
{
break;
}
m_Current=m_Current->next;
}
//在当前的所有节点链表中没有合适的,新分配吧
m_MemePollSize=sSize;
AllocPoolMemeroy();
returnFindMemeNode(sSize);
}
/*****************************************
计算所需的内存块数目
by风清扬song13-07-28
*****************************************/
voidCMemePool:
:
CalNeetLinkNumber(constsize_t&sSize)
{
floatfTempValue=sSize/m_MemLinkSize;
//向上取整需要的节点数目
m_Number=ceil(fTempValue);
if(0==m_Number)
{
m_Number=1;
}
}