最简单的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;
}
- 最简单的C内存池
- 一个最简单的内存池AutoMemory
- 最简单的C线程池
- 内存池的简单试验(C++)
- 内存池的简单试验(C++)
- 内存池的简单试验(C++)
- 内存池的简单试验(C++)
- 最简单的内存池-原理与实现
- c++:最简单的动态分配
- 最简单的socket c/s模型
- C#: 最简单的文件读写
- c/c++最简单的加密解密
- C语言最简单的文件操作
- 【C#】最简单的写日志DEMO
- 有史以来最简单的三层实例(C#)
- C/S最简单的socket通信
- 有史以来最简单的三层实例(C#)
- 最简单的c#Remoting编程
- 设计模式学习日志之十五:备忘录模式(原文转载)
- Unity支持的纹理格式
- 扬智(ALI)样片申请与经验
- 【ubuntu】Ubuntu 安装postgreSQL以及图形界面pgadmin3,hstore
- 用JTable显示数据库数据
- 最简单的C内存池
- TDK样片申请与经验
- 谈VC++对象模型
- Codeforces 216D Spider's Web 树状数组+模拟
- 研究VLC笔记1
- 在Ubuntu-12.04.2上编译安装MySQL-5.6.10
- 分布式计算、并行计算及集群、网格、云计算的区别
- 2013级测试赛 --E(Kruskal)
- 第01章 JSP内置对象