什么是内存池,这里简单介绍一下(不做详细说明),内存池技术是一种用于分配大量大小相同的小对象的技术,通过该技术可以极大加快内存分配/释放过程。其原理是先申请一大块内存,然后分成若干个大小相等的小块,用链表的方式将这些小块链在一起,当开发人员需要使用内存时(分配),从链表头取下一块返回给开发人员使用;当开发人员使用完毕后(释放),再将这块内存重新挂回链表尾。这样操作的好处有如下三点:1、提高分配和释放的效率;2、避免开发人员忘记释放内存造成内存泄露;3、减少内存占用(频繁的malloc是很占内存空间的,至于为什么我就不说了,可以到网上搜索内存管理相关的内容);
关于内存池技术的代码在网上也有不少,我也看过一些,有基于C现实的,也有C++实现的,但在这些文章里都发现两个问题,1、当开发人员使用完内存后,如果不释放,那么还是会有内存泄露的情况;2、在开发人员使用完内存后,释放的时候总会去循环判断该地址是否存在,在这种情况下,释放的效率并有没有得到提升。
那么如何解决这两个问题呢,我先简单介绍一下我这个内存池,先看段结构体:
- 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;
这里面描述了详细存放信息,当池初始化时,所有块都存在空闲块中,已使用块为空,当申请一块空间时,从空闲块的头取下来放到已使用的块中,基本过程就是这样子的。下面来解决上面提到的两个问题,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)
- {
- MemoryPool *p = &memory;
- MemoryBlock *pFree = NULL;//空闲块连表指针
- p->pBlockMemoryHead = (char *)malloc(sizeof(MemoryBlock) * blockCount);//分配一大块连续的内存块空间存放块信息
- p->pDataMemoryHead = (char *)malloc((blockSize + sizeof(MemoryBlock *)) * blockCount);//分配一大块连续的内存空间存放供用户使用的空间
- for(unsigned int i = 0; i < blockCount; i++)
- {
- pFree = (MemoryBlock *)(p->pBlockMemoryHead + (sizeof(MemoryBlock) * i));//(MemoryBlock *)malloc(sizeof(MemoryBlock));
- pFree->data = p->pDataMemoryHead + ((blockSize + sizeof(MemoryBlock *)) * i);//(void *)malloc(blockSize + sizeof(MemoryBlock *));//分配一块数据区域
- pFree->pNext = NULL;
- pFree->pPrev = p->freeTail;
- if(p->freeHead == NULL){
- p->freeHead = p->freeTail = pFree;
- }else{
- p->freeTail->pNext = pFree;
- p->freeTail = pFree;
- }
- }
- return 0;
- }
- //销毁内存池
- int Xpool_destroy(void)
- {
- MemoryPool *p = &memory;
- //释放内存块所占的内存
- free(p->pBlockMemoryHead);
- //释放数据区域所占的内存
- free(p->pDataMemoryHead);
- return 0;
- }
- //申请内存
- void* Xpool_alloc(unsigned int size)
- {
- MemoryPool *p = &memory;
- MemoryBlock *block = NULL;
- 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;//这个块的上个块是已使用块的最后一个块
- //第一次使用内存?
- 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 *);
- }
- //回收内存
- int Xpool_free(void *ptr)
- {
- MemoryPool *p = &memory;
- char *realptr = (char *)ptr - sizeof(MemoryBlock *); //数据块真实的起始地址
- MemoryBlock *block = (MemoryBlock *)realptr;
- if(block == NULL){
- return NULL;
- }
- 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++;
- return 0;
- }
- int main(int argc, char *argv[])
- {
- Xpool_init(10, 96);
- char *p = (char *)Xpool_alloc(20);
- Xpool_free(p);
- Xpool_destroy();
- return 0;
- }