最简单的C内存池

来源:互联网 发布:java public方法和类 编辑:程序博客网 时间:2024/05/16 12:30

最近经常遇到内存池的问题。

通过网上的资料进行改进。

什么是内存池,这里简单介绍一下(不做详细说明),内存池技术是一种用于分配大量大小相同的小对象的技术,通过该技术可以极大加快内存分配/释放过 程。其原理是先申请一大块内存,然后分成若干个大小相等的小块,用链表的方式将这些小块链在一起,当开发人员需要使用内存时(分配),从链表头取下一块返 回给开发人员使用;当开发人员使用完毕后(释放),再将这块内存重新挂回链表尾。这样操作的好处有如下三点:

1、提高分配和释放的效率;

2、避免开发人员 忘记释放内存造成内存泄露;

3、减少内存占用(频繁的malloc是很占内存空间的,至于为什么我就不说了,可以到网上搜索内存管理相关的内容);


关于内存池技术的代码在网上也有不少,我也看过一些,有基于C现实的,也有C++实现的,但在这些文章里都发现两个问题,

1、当开发人员使用完内存 后,如果不释放,那么还是会有内存泄露的情况;

2、在开发人员使用完内存后,释放的时候总会去循环判断该地址是否存在,在这种情况下,释放的效率并有没有 得到提升。
当池初始化时,所有块都存在空闲块中,已使用块为空,当申请一块空间时,从空闲块的头取下来放到已使用的块中,基本过程 就是这样子的。

下面来解决上面提到的两个问题,

1、在内存泄露方面,先申请两大块的连续空间,一块用于存放块的基本信息(块链表),另一块返回给开发人员 使用(数据区域),然后用指针保存两块内存地址,这样一来,不管你是否释放,在程序需要清理内存的时候,我只需要free两次;

2、在释放效率方面,我们 不使用循环方式来处理,而是在返回给开发人员使用的内存块前面加4个字节存放与他关联的块信息块地址,

在释放的时候,我们只需要(MemoryBlock *)((char *)ptr – 4)就能得到块地址,这样就很方便的挂回空闲块,这一点没有实现全面,我自已把这点去掉了,目的就是实现最简单的内存池。


下面是具体的代码,运行通过。

#include <stdio.h>
#include <malloc.h>
#include <string.h>


typedef struct _memory_block
{
struct _memory_block *pNext;//下一个内存单元 
struct _memory_block *pPrev;//上一个内存单元 
char * data;//数据 
} MemoryBlock;


typedef struct _memory_pool
{
unsigned int blockCount;//申请块数量 
unsigned int blockCountStep;//内存块数增长步长 
unsigned int blockSize;//单个块的大小 
unsigned int freeCount;//空闲的内存块数 
MemoryBlock *freeHead;//空闲的头 
MemoryBlock *freeTail;//空闲的尾 
MemoryBlock *usedHead;//已使用的头 
MemoryBlock *usedTail;//已使用的尾 
char * pBlockMemoryHead;//块的头指针,用于释放内存时候用 
char * pDataMemoryHead;//数据区域头指针 
} MemoryPool;


int Xpool_init(unsigned int blockCount,unsigned int blockSize);


int Xpool_destroy(void);


void * Xpool_alloc(unsigned int size);


int Xpool_free(void *ptr);


static int Xpool_block(unsigned int blockCount,unsigned int blockSize);


static MemoryPool memory;


//初始化内存池 
int Xpool_init(unsigned int blockCount ,unsigned int blockSize)
{
MemoryPool *p=&memory;
p->blockCount=blockCount;
p->blockSize=blockSize;
p->freeCount=blockCount;
p->blockCountStep=100;
p->freeHead=p->freeTail=NULL;
p->usedHead=p->usedTail=NULL;
Xpool_block(blockCount,blockSize);
return 0;
}


//申请快,并且把新申请的块连到空闲块后面 
static int Xpool_block(unsigned int blockCount,unsigned int blockSize)
{
printf("进入函数Xpool_block\n");
MemoryPool *p=&memory;
MemoryBlock *pFree=NULL;//空闲块链表指针 
p->pBlockMemoryHead=(char *)malloc(sizeof(MemoryBlock)*blockCount);//分配一大块连续的内存块空间存放块信息 
//p->pDataMemoryHead=(char *)malloc((blockSize+sizeof(MemoryBlock *))*blockCount);//分配一大块连续的内存空间存放供用户使用的空间 
p->pDataMemoryHead=(char *)malloc((blockSize)*blockCount);//分配一大块连续的内存空间存放供用户使用的空间 
printf("p->pDataMemoryHead=%p \n",p->pDataMemoryHead);

for(unsigned int i=0;i<blockCount;i++)
{
pFree=(MemoryBlock *)(p->pBlockMemoryHead+(sizeof(MemoryBlock)*i));
//pFree->data=p->pDataMemoryHead+((blockSize+sizeof(MemoryBlock *))*i);//分配一块数据区域 
pFree->data=p->pDataMemoryHead+((blockSize)*i);//分配一块数据区域 
//memcpy(pFree->data,pFree,sizeof(MemoryBlock *));
pFree->pNext=NULL;
pFree->pPrev=p->freeTail;

if(p->freeHead==NULL)
{
pFree->pPrev=NULL;
p->freeHead=p->freeTail=pFree;
printf("p->freeHead=%p \n",p->freeHead);
printf("pFree->data=%p \n",pFree->data);

}
else
{
p->freeTail->pNext=pFree;
p->freeTail=pFree;
}
}
printf("退出函数Xpool_block\n");
return 0;
}


//销毁内存池
int Xpool_destroy(void)
{
MemoryPool *p=&memory;
//释放内存块所占的内存 
free(p->pBlockMemoryHead);
//释放数据区域所占的内存 
free(p->pDataMemoryHead);

return 0;



//申请内存 
void *Xpool_alloc(unsigned int size)
{
printf("进入函数Xpool_alloc\n");
MemoryPool *p=&memory;
MemoryBlock *block=NULL;
printf("p->freeHead=%p \n",p->freeHead);
if(p->freeHead==NULL)//没有可用空间 
{
Xpool_block(p->blockCountStep,p->blockSize);
}
block=p->freeHead;//获取表头内存块 
p->freeHead=block->pNext;//将空闲块的链表头 
p->freeCount--;//空闲块数量减一 
block->pNext=NULL;
block->pPrev=p->usedTail;//这个块的上个块是已使用块的最后一个块 
printf("block->pPrev=%p\n",block->pPrev);
//第一次使用内存? 
if(p->usedHead==NULL)
{
p->usedHead=p->usedTail=block;//,则已使用的头和尾都指向这个块 
}
else//不是第一次 
{
p->usedTail->pNext=block;
p->usedTail=block;
}
//留下data里一个指针的空间,用于保存与数据关联的块地址 
//block->data=(char *)block;
//return (char *)block->data+sizeof(MemoryBlock *);
printf("block->pPrev=%p\n",block->pPrev);
printf("block->data=%p\n",block->data);
printf("退出函数Xpool_alloc\n");
//return (char *)block->data+sizeof(MemoryBlock *);
return (char *)block->data;
}


//回收内存 
int Xpool_free(void *ptr)
{
printf("进入函数Xpool_free\n");
MemoryPool *p=&memory;
//char * realptr=(char *)ptr-sizeof(MemoryBlock *);//数据块真实的起始地址 
char * realptr=(char *)ptr;//数据块真实的起始地址 
MemoryBlock *block1=p->usedHead;
printf("realptr=%p \n",realptr);
printf("p->usedHead=%p \n",p->usedHead);
printf("block1->data=%p \n",block1->data);
while(block1->data!=realptr)
{
block1=block1->pNext;
printf("block1=%p \n",block1);
}

printf("block1=%p \n",block1);
MemoryBlock *block=(MemoryBlock *)block1;
printf("block=%p \n",block);
if(block == NULL)
{
return NULL;
}
printf("block->pPrev=%p \n",block->pPrev);
if(block->pPrev == NULL)//如果是头 
{
p->usedHead=block->pNext;
if(p->usedHead!=NULL)
{
p->usedHead->pPrev=NULL;
}
}
else if(block->pNext == NULL)//如果是尾 
{
p->usedTail=block->pPrev;
if(p->usedTail!=NULL)
{
p->usedTail->pNext=NULL;
}
}
else//中间的 
{
block->pPrev->pNext=block->pNext;
block->pNext->pPrev=block->pPrev;
}

//重置参数
block->pPrev=p->freeTail;
block->pNext=NULL;
block->data=realptr;

//加到空闲块链表
p->freeTail->pNext=block;
p->freeTail=block;
p->freeCount++;
printf("退出函数Xpool_free\n");
return 0; 
}




int main(int argc,char *argv[])
{
Xpool_init(10,96);
char *p=(char *)Xpool_alloc(20);
printf("p=%p\n",p);
strcpy(p,"123456789");
printf("p=%s \n",p);
Xpool_free(p);
Xpool_destroy();
return 0;
}

0 0