程序在向系统申请内存时,很容易造成内存碎片,影响程序性能。内存池是一种内存分配策略,程序在之前,先向系统申请一大块内存,并将这一大块内存分割成若干小的内存块,供程序使用。当内存不足时再向系统申请。
1、程序以1024位为界,将内存块block分为两种,一种是普通的block,可以直接向内存池申请,一种是大block,需要向系统申请;
2、MemoryChunk中是一系列大小相等的block块,块的大小和数量都在初始化时给定,块的组织方式是链表结构,在分配或回收时需要考虑并发安全性;
3、StaticMemory中是一系列大小MemoryChunk,这些Chunk的组织方向是数组形式,不同Chunk中,block的大小不等,向内存池申请内存时,会在合适大小的chunk中取出一块block返回给用户;
#ifndef MEMORY_CHUNK
#define MEMORY_CHUNK
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
struct MemoryBlock;
typedef struct BlockHeader{//头部
MemoryBlock *next;
size_t length;
}BlockHeader;
typedef struct BlockData{//数据
char buffer;
}BlockData;
//Block的定义,本质上是一个链表结构
typedef struct MemoryBlock{
BlockHeader header;//头部
BlockData data;//数据部分
}MemoryBlock;
//Chunk是一系列Block的管理者
class MemoryChunk{//维护block及回收释放操作
public:
//初始化里构建一定大小和数量的Block
MemoryChunk(size_t size,size_t num)
{
pthread_mutex_init(&mutex,NULL);//互斥量初始化
blockSize=size;//Block大小的初始化
phead=NULL;//头指针
MemoryBlock* pblock;
blockNum=0;//Block块数大小的初始化,分配成功里再计数
while(blockNum<num)
{
if((pblock=createBlock())!=NULL)
{//分配成功,将Block加入到链表当中
(pblock->header).next=phead;//下一指针
(pblock->header).length=blockSize;//Block大小初始化
phead=pblock;
blockNum++;//Block块数增1
}
else
{
break;
}
}
}
~MemoryChunk()
{
MemoryBlock* pblock;
while(phead)
{
pblock=phead;
phead=(phead->header).next;
::free(pblock);//将申请的内存归还给系统
pblock=NULL;
blockNum--;
}
assert(blockNum==0);//确保内存释放完全
pthread_mutex_destroy(&mutex);
}
void* malloc()//分配内存
{
MemoryBlock* pBlock=NULL;
pthread_mutex_lock(&mutex);
if(phead)
{//Chunk中有足够的Block,则直接分配
pBlock=phead;
phead=(phead->header).next;
blockNum--;//总数量减1
}
else
{//重新申请一块Block
pBlock=createBlock();
}
pthread_mutex_unlock(&mutex);
return &((pBlock->data).buffer);
}
void free(void* pmem)//回收内存
{
MemoryBlock* pblock;
//对内存区域的操作,很重要
pblock=(MemoryBlock*)((char*)pmem-sizeof(BlockHeader));
free(pblock);
}
void free(MemoryBlock* pblock)
{//回收到内存池当中
pthread_mutex_lock(&mutex);
(pblock->header).next=phead;
phead=pblock;
blockNum++;//Block问题增1
pthread_mutex_unlock(&mutex);
}
size_t getBlockSize()//获取Chunk中block的大小
{
return blockSize;
}
size_t getBlockNum()//获取chunk中block的数量
{
return blockNum;
}
protected:
MemoryBlock* createBlock()//从系统当中获取内存
{
MemoryBlock* result;
//sizeof返回的是字节数,blockSize存放的是位数
result=(MemoryBlock*)::malloc(sizeof(BlockHeader)*8+blockSize);
return result;
}
private:
MemoryBlock* phead;//头指针
size_t blockSize;//Block大小
size_t blockNum;//Block的数量
pthread_mutex_t mutex;//互斥量
};
#endif //MEMORY_CHUNK
#ifndef STATICMEMORY
#define STATICMEMORY
#include"MemoryChunk.h"
typedef struct BigBlockHeader{
size_t length;
}BigBlockHeader;
//大Block的结构
typedef struct BigBlock{
BigBlockHeader header;
char buffer;
}BigBlock;
//StaticMemory中存放的是不同大小的Chunk
class StaticMemory{
public:
//Block的大小范围,最小为1字节
enum{MAX_SIZE=1024,MIN_SIZE=8};
StaticMemory()
{
count=0;//Chunk的数量
for(size_t size=MIN_SIZE;size<MAX_SIZE;size*=2)
++count;
//构建Chunk的列表,内部都是指向Chunk的指针
pChunkList=new MemoryChunk*[count];
int index=0;
for(size_t size=MIN_SIZE;size<MAX_SIZE;size*=2)
{
//每个Chunk中存放100个Block
pChunkList[index++]=new MemoryChunk(size,100);
}
}
~StaticMemory()
{
for(size_t index=0;index<count;++index)
{//释放每一个Chunk指针,并调用其析构函数
delete pChunkList[index];
}
delete [] pChunkList;
}
void *Malloc(size_t size)
{
if(size>MAX_SIZE)
{//调用大Block的创建方法,大Block不在内存池内申请
malloc(size);
}
else
{
int index=0;
for(size_t tsize=MIN_SIZE;tsize<MAX_SIZE;tsize*=2)
{//寻找合适大小的Block
if(tsize>=size)
break;
index++;
}
return pChunkList[index]->malloc();//分配内存
}
return NULL;
}
void Free(void* pMem)
{
if(!free(pMem))
{//如果不是大Block
MemoryBlock* pblock;
pblock=(MemoryBlock*)((char*)pMem-sizeof(BlockHeader));
int index=0;
for(size_t size=MIN_SIZE;size<MAX_SIZE;size*=2)
{
if((pblock->header).length==size)
break;
index++;
}
pChunkList[index]->free(pMem);
}
}
size_t getCount()
{
return count;
}
protected:
void* malloc(size_t size)
{
BigBlock* bblock;
bblock=(BigBlock*)::malloc(sizeof(BigBlockHeader)*8+size);
if(bblock)
{
(bblock->header).length=size;
return &(bblock->buffer);
}
return NULL;
}
bool free(void* pMem)
{//大Block的结构和普通block头部的结构有所区别,普通block前4字节
//是一个指针,接下来4个字节是一个size_t类型变量
//而大Block的头部中,只有一个size_t变量表示长度
//所以两者头部长度是不等的,当为了判断一个block是否是大block
//取长度时就应该注意指针的回退位置
BigBlock* bblock;
bblock=(BigBlock*)((char*)pMem-sizeof(BigBlockHeader));
if((bblock->header).length>MAX_SIZE)
{
::free(bblock);
return true;
}
return false;
}
private:
MemoryChunk** pChunkList;//Chunk列表
size_t count;//不同大小的Chunk数量
};
#endif //STATICMEMORY