c语言内存池中 二维数组,一个简单实用的内存池实现之二 (C实现)

上一篇内存池的实现其实更像一个后备列表的实现。使用上来说不是很方便,要申请的内存块是一个BLOCK结构的一个个成员,而且每次从系统内存堆中申请都是一小块一小块,也没有考虑字节对齐。因此让我们来看看新的一个内存池的实现吧。

这个内存池是根据《c++应用程序性能优化》书里的固定尺寸的内存池原理做了一些改动用C语言写的。大家有兴趣可以去看看,里面说的最详细。

简单说下这个内存池的原理,内存池里由N个memblock以一个双向链表组成,每个memblock的组成是一个HEAD块+M个固定长度的memchunk组成,memchunk就是你将来要从池中申请的内存块。

我们来看下如下几个情况:

1.内存池初始化后,内存池的memblock链表头是NULL。

2.第一次从池中申请一个memchunk,内存池根据initsize和chunksize从系统内存堆中申请一个(memblock head)+ chunksize*initsize的内存块,对block head部分数据字段进行初始化,并将每个chunk的头4个字节来存放该memblock里下个可用chunk的编号,因为是固定长度的chunk,所以,可以很容易根据编号和chunk长度计算出chunk的地址。创建了memblock后,将第一个chunk (设为A) 返回给用户,并将block head的first字段设置为chunk A头4个字节的值(也就是下个可用chunk编号)。同时将创建的block加入到链表头中。

3.下次申请memchunk的时候,遍历链表,找出有空闲chunk的BLOCK,对BLOCK进行和第一次申请时类似的处理。

同时检查该BLOCK里还有多余的空闲chunk不,有的话就将该block移动到链表头部。以提高下次申请时遍历链表的速度。

如果遍历完链表也没有找到有空闲chunk的block,就从系统内存堆中申请一个BLOCK,将之加入到链表头。

4.将申请的memchunk (假设为A)归还给池的时候,遍历memblock链表,根据A的地址来找出A所在的block。

找到后根据这个 memchunk A 的地址计算出它的编号;

将block->first 的编号存入A的头4个字节中; 将block->first更改为A的编号。(就是chunk的链表操作)

最后,将A所在的这个memblock移动到链表头(因为有空闲chunk),以提高申请chunk时的速度。(链表只需遍历一次)。在书中,这里还有个处理:如果该block的chunk都是空闲的,就把block释放了(归还给系统内存堆),我没有这样做,打算单独写个清理的操作。

大概原理就是这样,考虑到和64位机兼容,chunk和block都按8字节对齐。代码中的memheap就是mempool。只是名称我该成heap了。。

在后面的代码中,对内存池实现有比较详细的注释。

回顾下这个内存池的原理,明显的优点是减少了内存碎片,字节对齐,但是有个显而易见的问题是,如果内存池中有大量(成千上万)个memblock的话,对block的遍历检索将是一个性能瓶颈,申请chunk的操作还好点,内部做了一些优化处理,归还chunk时查找链表的速度将比较慢,最坏的情况是有多少个memblock就检索多少次。。可以考虑对这里做一些检索上的优化和更改,不用双向链表,用其他方式来做。最简单的优化就是用游戏粒子系统里普遍使用的一种算法,将有空闲chunk的block放一个链表,没有空闲chunk的block放另外一个链表,再做一些分配上的改动,也许能提高一些速度。

mempool.h

2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*********************************

df37983f39daa189b8c814e01a6a9011.png * mempool

0ac3a2d53663ec01c7f7225264eeefae.png ********************************/cbef093dcc044b2793832001e2365e43.png#defineMEMPOOL_ALIGNMENT 8//兼容64位系统,按8字节对齐cbef093dcc044b2793832001e2365e43.png

cbef093dcc044b2793832001e2365e43.pngstructmemblock

2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.png    uint32_t        size;//该Block下chunk内存总长度;df37983f39daa189b8c814e01a6a9011.pnguint32_t        free;//空闲chunk数df37983f39daa189b8c814e01a6a9011.pnguint32_t        first;//第一个空闲chunk iddf37983f39daa189b8c814e01a6a9011.pnguint32_t        dumpalign;//按8字节对齐,只是占位用df37983f39daa189b8c814e01a6a9011.pngstructmemblock*next_block;//指向下个Blockdf37983f39daa189b8c814e01a6a9011.pngstructmemblock*prev_block;//指向上个Blockdf37983f39daa189b8c814e01a6a9011.pngchardata[1];//chunk区首地址0ac3a2d53663ec01c7f7225264eeefae.png};

cbef093dcc044b2793832001e2365e43.png

cbef093dcc044b2793832001e2365e43.pngstructmemheap

2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.pngstructmemblock*block_header;//Block双向链表头df37983f39daa189b8c814e01a6a9011.pnguint32_t        chunk_size;//chunk大小df37983f39daa189b8c814e01a6a9011.pnguint32_t        init_size;//第一次创建Block时的chunk数df37983f39daa189b8c814e01a6a9011.pnguint32_t        grow_size;//之后创建Block时的chunk数

df37983f39daa189b8c814e01a6a9011.png//uint32_t        max_size;//最大memory使用

df37983f39daa189b8c814e01a6a9011.png//uint32_t        blocknum;0ac3a2d53663ec01c7f7225264eeefae.png};

cbef093dcc044b2793832001e2365e43.png

cbef093dcc044b2793832001e2365e43.png//create and init struct memheap,返回memheap指针cbef093dcc044b2793832001e2365e43.pngvoid*memheap_init(uint32_t chunksize,uint32_t initsize,uint32_t growsize);

cbef093dcc044b2793832001e2365e43.png

cbef093dcc044b2793832001e2365e43.png//destruct memheapcbef093dcc044b2793832001e2365e43.pngvoidmemheap_dealloc(structmemheap*pool);

cbef093dcc044b2793832001e2365e43.png

cbef093dcc044b2793832001e2365e43.png//从内存池申请一块长度为chunk_size的内存cbef093dcc044b2793832001e2365e43.pnginline

cbef093dcc044b2793832001e2365e43.pngvoid*memheap_alloc(structmemheap*pool);

cbef093dcc044b2793832001e2365e43.png

cbef093dcc044b2793832001e2365e43.png//向内存池归还一块内存,成功则返回NULLcbef093dcc044b2793832001e2365e43.pnginline 

cbef093dcc044b2793832001e2365e43.pngvoid*memheap_free(structmemheap*pool,void*p);

cbef093dcc044b2793832001e2365e43.png

cbef093dcc044b2793832001e2365e43.png//清理内存池多余的内存cbef093dcc044b2793832001e2365e43.pngvoidmemheap_clean(structmemheap*pool,void*p);

mempool.c

cbef093dcc044b2793832001e2365e43.png2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png/**//*********************************

df37983f39daa189b8c814e01a6a9011.png * mempool

0ac3a2d53663ec01c7f7225264eeefae.png ********************************/cbef093dcc044b2793832001e2365e43.png//将一个block从链表中移动到首部cbef093dcc044b2793832001e2365e43.png#defineMEMBLOCK_MOVE_TO_HEAD(HEAD,BLOCK)  \2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.pngif((BLOCK)!=(HEAD))918e8df969f9f8c8d002f25cda86cade.png{ \

df37983f39daa189b8c814e01a6a9011.pngstructmemblock*prev=(BLOCK)->prev_block; \

df37983f39daa189b8c814e01a6a9011.pngstructmemblock*next=(BLOCK)->next_block; \

df37983f39daa189b8c814e01a6a9011.pngif(prev) prev->next_block=next;\

df37983f39daa189b8c814e01a6a9011.pngif(next) next->prev_block=prev;\

df37983f39daa189b8c814e01a6a9011.png    (BLOCK)->prev_block=NULL;\

df37983f39daa189b8c814e01a6a9011.png    (BLOCK)->next_block=(HEAD);\

df37983f39daa189b8c814e01a6a9011.png    (HEAD)->prev_block=(BLOCK); \

0ac3a2d53663ec01c7f7225264eeefae.png    (HEAD)=(BLOCK);     }cbef093dcc044b2793832001e2365e43.png    

cbef093dcc044b2793832001e2365e43.png

cbef093dcc044b2793832001e2365e43.png//-----------------declare-----------------

cbef093dcc044b2793832001e2365e43.png//创建一个Blockcbef093dcc044b2793832001e2365e43.pngstaticinlinevoid*memblock_create(uint32_t chunksize,uint32_t num);

cbef093dcc044b2793832001e2365e43.png

cbef093dcc044b2793832001e2365e43.png//------------------implement---------------cbef093dcc044b2793832001e2365e43.pngstaticinlinevoid*cbef093dcc044b2793832001e2365e43.pngmemblock_create(uint32_t chunksize,uint32_t num)

2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.png//memblock长度df37983f39daa189b8c814e01a6a9011.pnguint32_t length=sizeof(structmemblock)-sizeof(char*)+num*chunksize;

df37983f39daa189b8c814e01a6a9011.pngstructmemblock*block=G_MALLOC(length);

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.pngif(block==NULL)918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.png    L_WARN("%s,malloc error.",__func__);

df37983f39daa189b8c814e01a6a9011.pngreturn(NULL);

4a5daaec04350a363f186a4d2c5ed6ce.png    }df37983f39daa189b8c814e01a6a9011.png    

df37983f39daa189b8c814e01a6a9011.png    block->size=num*chunksize;

df37983f39daa189b8c814e01a6a9011.png    block->free=num-1;//因为创建后就分配出去,所以空闲chunk数num-1df37983f39daa189b8c814e01a6a9011.pngblock->first=1;//同上,指向第二个chunkdf37983f39daa189b8c814e01a6a9011.pngblock->next_block=NULL;

df37983f39daa189b8c814e01a6a9011.png    block->prev_block=NULL;

df37983f39daa189b8c814e01a6a9011.png   

df37983f39daa189b8c814e01a6a9011.png//初始化chunk编号,每个chunk头4个字节存放下个可用chunk的编号。df37983f39daa189b8c814e01a6a9011.pngchar*offset=block->data;

df37983f39daa189b8c814e01a6a9011.png    uint32_t i=num-1;

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.pngfor(i=1;i{

df37983f39daa189b8c814e01a6a9011.png*((uint32_t*)offset)=i;

df37983f39daa189b8c814e01a6a9011.png    offset+=chunksize;

4a5daaec04350a363f186a4d2c5ed6ce.png    }df37983f39daa189b8c814e01a6a9011.pngreturn(block);

0ac3a2d53663ec01c7f7225264eeefae.png}cbef093dcc044b2793832001e2365e43.png

cbef093dcc044b2793832001e2365e43.pnginlinevoid*cbef093dcc044b2793832001e2365e43.pngmemheap_alloc(structmemheap*pool)

2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.pngstructmemblock*block=pool->block_header;

df37983f39daa189b8c814e01a6a9011.png

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.pngif(block==NULL)918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.png//链表头为空,第一次创建一个Block;并返回该block的第一个chunkdf37983f39daa189b8c814e01a6a9011.pngblock=memblock_create(pool->chunk_size , pool->init_size);

df37983f39daa189b8c814e01a6a9011.png    pool->block_header=block;

df37983f39daa189b8c814e01a6a9011.png

df37983f39daa189b8c814e01a6a9011.pngreturn(block->data);

4a5daaec04350a363f186a4d2c5ed6ce.png    }df37983f39daa189b8c814e01a6a9011.png

df37983f39daa189b8c814e01a6a9011.png//查找有空闲chunk的Blockdf37983f39daa189b8c814e01a6a9011.pngwhile(block!=NULL&&block->free==0)

df37983f39daa189b8c814e01a6a9011.png    block=block->next_block;

df37983f39daa189b8c814e01a6a9011.png     

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.pngif(block)918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.png//找到一块block,根据block->first计算出空闲chunk的地址df37983f39daa189b8c814e01a6a9011.pngchar*mem=block->data+(block->first*pool->chunk_size);

df37983f39daa189b8c814e01a6a9011.png//更改first为找到的chunk的开始4个字节存放的编号df37983f39daa189b8c814e01a6a9011.pngblock->first=*((uint32_t*)mem);

df37983f39daa189b8c814e01a6a9011.png    block->free--;//空闲chunk数减一

df37983f39daa189b8c814e01a6a9011.png//将有空闲chunk的Block移动到链表头部f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.pngif(block!=pool->block_header&&block->free>pool->block_header->free)918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.png        MEMBLOCK_MOVE_TO_HEAD(pool->block_header,block)  

4a5daaec04350a363f186a4d2c5ed6ce.png    }df37983f39daa189b8c814e01a6a9011.png    

df37983f39daa189b8c814e01a6a9011.pngreturn(mem);//分配出去的chunk的开始4个字节的内容无用了4a5daaec04350a363f186a4d2c5ed6ce.png}f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.pngelse918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.png//没有找到有空闲chunk的block。创建一个Block,并将之加入到链表头df37983f39daa189b8c814e01a6a9011.pngblock=memblock_create(pool->chunk_size , pool->grow_size);

df37983f39daa189b8c814e01a6a9011.png    

df37983f39daa189b8c814e01a6a9011.png    block->next_block=pool->block_header;

df37983f39daa189b8c814e01a6a9011.png    pool->block_header->prev_block=block;

df37983f39daa189b8c814e01a6a9011.png    pool->block_header=block;

df37983f39daa189b8c814e01a6a9011.png    

df37983f39daa189b8c814e01a6a9011.pngreturn(block->data);

4a5daaec04350a363f186a4d2c5ed6ce.png    }0ac3a2d53663ec01c7f7225264eeefae.png}cbef093dcc044b2793832001e2365e43.png

cbef093dcc044b2793832001e2365e43.pnginlinevoid*cbef093dcc044b2793832001e2365e43.pngmemheap_free(structmemheap*pool,void*p)

2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.pngstructmemblock*block=pool->block_header;

df37983f39daa189b8c814e01a6a9011.png//更加p地址值找出p所在的Blockdf37983f39daa189b8c814e01a6a9011.pngwhile(block&&(pdata||df37983f39daa189b8c814e01a6a9011.png       p>=(void*)block->data+block->size))

df37983f39daa189b8c814e01a6a9011.png    block=block->next_block;

df37983f39daa189b8c814e01a6a9011.png

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.pngif(block==NULL)918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.png    L_WARN("%s,no memblock find",__func__);    

df37983f39daa189b8c814e01a6a9011.pngreturn(p);

4a5daaec04350a363f186a4d2c5ed6ce.png    }df37983f39daa189b8c814e01a6a9011.png

df37983f39daa189b8c814e01a6a9011.png    uint32_t offset=p-(void*) block->data;

df37983f39daa189b8c814e01a6a9011.png#ifndef NDEBUG 

df37983f39daa189b8c814e01a6a9011.png//检查p是否指向一个合法的chunk首地址 

df37983f39daa189b8c814e01a6a9011.png//chunk_size肯定是偶数,使用与运算实现取模

df37983f39daa189b8c814e01a6a9011.png//offset % pool->chunk_sizef70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.pngif((offset&(pool->chunk_size-1))>0)918e8df969f9f8c8d002f25cda86cade.png{    

df37983f39daa189b8c814e01a6a9011.png    L_ERROR("%s,invalid pointer for free.",__func__);

df37983f39daa189b8c814e01a6a9011.pngreturn(p);

4a5daaec04350a363f186a4d2c5ed6ce.png    }df37983f39daa189b8c814e01a6a9011.png#endifdf37983f39daa189b8c814e01a6a9011.png//设置Blockdf37983f39daa189b8c814e01a6a9011.pngblock->free++;//空闲chunk数加一df37983f39daa189b8c814e01a6a9011.png*((uint32_t*)p)=block->first;//修改归还的chunk的头4个字节的值df37983f39daa189b8c814e01a6a9011.pngblock->first=(uint32_t)(offset/pool->chunk_size);//first指向归还的chunk

df37983f39daa189b8c814e01a6a9011.png

df37983f39daa189b8c814e01a6a9011.png//将Block移动到链表头部df37983f39daa189b8c814e01a6a9011.pngMEMBLOCK_MOVE_TO_HEAD(pool->block_header,block)  

df37983f39daa189b8c814e01a6a9011.png

df37983f39daa189b8c814e01a6a9011.pngreturn(NULL);

0ac3a2d53663ec01c7f7225264eeefae.png}cbef093dcc044b2793832001e2365e43.png

cbef093dcc044b2793832001e2365e43.png

cbef093dcc044b2793832001e2365e43.pngvoid*cbef093dcc044b2793832001e2365e43.pngmemheap_init(uint32_t chunksize,uint32_t initsize,uint32_t growsize)

2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.pngif(!initsize||!growsize)return(NULL);

df37983f39daa189b8c814e01a6a9011.png    

df37983f39daa189b8c814e01a6a9011.pngstructmemheap*pool=G_MALLOC(sizeof(structmemheap));

df37983f39daa189b8c814e01a6a9011.png

df37983f39daa189b8c814e01a6a9011.png//保证chunk size最小能存放一个uint32_t大小的数df37983f39daa189b8c814e01a6a9011.pngif(chunksize

df37983f39daa189b8c814e01a6a9011.png//更改chunk size字节对齐(8字节)df37983f39daa189b8c814e01a6a9011.pngpool->chunk_size=(chunksize+(MEMPOOL_ALIGNMENT-1))&~(MEMPOOL_ALIGNMENT-1);

df37983f39daa189b8c814e01a6a9011.png

df37983f39daa189b8c814e01a6a9011.png    pool->block_header=NULL;

df37983f39daa189b8c814e01a6a9011.png    pool->init_size=initsize;

df37983f39daa189b8c814e01a6a9011.png    pool->grow_size=growsize;

df37983f39daa189b8c814e01a6a9011.png//pool->max_size=0;

df37983f39daa189b8c814e01a6a9011.png//pool->blocknum=0;df37983f39daa189b8c814e01a6a9011.pngreturn(pool);

0ac3a2d53663ec01c7f7225264eeefae.png}cbef093dcc044b2793832001e2365e43.png

cbef093dcc044b2793832001e2365e43.pngvoidcbef093dcc044b2793832001e2365e43.pngmemheap_dealloc(structmemheap*pool)

2f88ce130b654eb5dc6788e02dbcfc90.png

dbf989d57862681739b642d8621fe1f0.png918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.png

df37983f39daa189b8c814e01a6a9011.png   

df37983f39daa189b8c814e01a6a9011.pngstructmemblock*block=pool->block_header;

df37983f39daa189b8c814e01a6a9011.pngstructmemblock*temp=NULL;

f70a0fde2b51b7dd92a70e712e540cf6.png

edb48e6f68462ea23d9a824f01de40c5.pngwhile(block)918e8df969f9f8c8d002f25cda86cade.png{

df37983f39daa189b8c814e01a6a9011.png    temp=block;

df37983f39daa189b8c814e01a6a9011.png    block=block->next_block;

df37983f39daa189b8c814e01a6a9011.png    G_FREE(temp);    

4a5daaec04350a363f186a4d2c5ed6ce.png    }df37983f39daa189b8c814e01a6a9011.png    G_FREE(pool);

0ac3a2d53663ec01c7f7225264eeefae.png}cbef093dcc044b2793832001e2365e43.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值