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