最近,一直想写自己的内存池,也参考了网上很多的内存池例子,就写了自己的第一个内存池,因为网上大部分的内存池都是全局内存池,因为公司的项目中每个单位是房间,那么我就在想能不能写一个,适合自己项目的一个分配快捷,又不会产生过多内存碎片的内存池。于是,我自己写了一个内存池,新手代码,请勿介意!
下面是我自己写的内存池大概结构:
由于这个结构在网上已经有很多更加详细的描述,那么我就在这里简单的介绍一下各个模块的功能:
MChunk:内存池的块核心结构,对原始的内存进行整理
MBlock_t:内存池中的内存块,已经对原始的系统内存进行一次整理,用于直接分配给调用者使用
MCBlock:每个内存池块数组的起始地址,用于快速确定这个内存池数组使用情况
主要思路:
1、在创建此内存池时候,程序会根据传入的最小内存块大小min_alloc_size,最大的内存块大小max_alloc_size,以及内存池容量max_mem,进行运算
2、由于系统底层实际上是以二进制位作为单位表示所有的数据,所以我们也顺应系统的规则,将传入的数据进行校正
min_alloc_size>= max{2^i,i>=0}
max_alloc_size<= min{2^i,i>=0}
max_mem<= min{2^i,i>=0}
3、根据校正后的边界进行计算每一块区域需要分配内存块总数目alloc_count,将其初始化
4、每一个chunk区域中都有一个buf指针,ZS_Mem对buf指针指向的一片内存初始化为一块块紧密相连的内存块数组MBlock_t,要注意的是其数据结构不是链表。因为由链表组成的内存池在寻址过程中会比数组寻址慢。此外,每一块MBlock_t的pHead都是指向MBlock数组的首地址。
5、MBlock的存在是为了更好的管理MBlock_t,用于计算这片区域的内存使用情况
6、用户在使用此内存池的时候,要使用最佳匹配原则进行分配
分析:
此内存池缺点:
1、每次分配内存都会多16字节的MBlock_t
2、没有使用引用计数功能,用户需要手动释放内存池数据(打算以后添加)
此内存池特点:
1、在windows平台运行速度会比普通的new malloc快,但是在linux平台因为与其分配策略有部分相同,所以效率差别不大
2、可以更加好的管理程序申请的内存
3、ZS_Mem会根据用户在初始化时候传入的内存块申请的最大值、最小值、以及内存池容量进行相应的初始化内存块
在window上ZS_Mem(release版本)在运行效率:
因为同样在100,000,000的随机分配情况,new和malloc都会特别的慢,所以只测试了1,000,000的情况。
注意:不同的环境可能存在着差异
这里,我先上代码,然后再对代码必要的地方进行剖析,而且只是一个初稿,更多的完善需要大家给意见来完成
#pragma once
/*!
* \file ZSMemoryPool.h
*
* \author judy
* \date 二月 2017
*
* 私有内存池
*/
/*!
* \class MemoryBlock
*
* \brief 定义内存块核心结构
*
* \author judy
* \date 二月 2017
*/
#define MEM_IS_USED 0xFFFF
#define MEM_IS_FREE 0x7FFF
#define EXTRA_MEM 16//额外16个字节
#define MEM_OFFSER(BASE_ADDR) (char*)BASE_ADDR+EXTRA_MEM
typedef struct MemoryBlock_t
{
int iLength;
int isUsed;
void *pHead;
void *pBuffer;
}MBlock_t;
typedef struct MemoryBlock
{
int free_cnt;
int use_cnt;
void * start_adr;
}MBlock;
/*!
* \class MemoryChunk
*
* \brief 定义块核心结构,多个MemoryBlock组成一个MemoryChunk
*
* \author judy
* \date 二月 2017
*/
typedef struct MemoryChunk
{
MemoryChunk * pNext;
int size;
void *buf;
}MChunk;
/*!
* \class ZSMemoryPool
*
* \brief SLAB算法无锁内存池类,通过使用该类获取
*
* \author judy
* \date 二月 2017
*/
class ZSMemoryPool
{
public:
ZSMemoryPool(size_t min_alloc_size, size_t max_alloc_size, size_t max_mem);
//内存池初始化函数
int ZSPoolInit();
//内存池申请内存接口
void * ZSAlloc(size_t alloc_size);
//内存池释放内存接口
int ZSFree(void * _freePtr);
//内存池释放所有资源接口
int ZSDestroy();
protected:
ZSMemoryPool();
ZSMemoryPool(const ZSMemoryPool&);
~ZSMemoryPool();
private:
int add_head(MChunk * ck);
//初始化的内存进行分配并且定位下标
int ZSPoolMemInit(size_t _Ssize, size_t _Esize, size_t _Tsize);
private:
//8 16 32 62 128 256 512 1k 2k 4k 8k 16k 32k 64k
MChunk * trunks; //块核心结构
size_t bk_cnt; //每区域SLAB的数目
MBlock * blocks; //内存数组
size_t _distance; //最大最小内存块距离2^n次幂
size_t min_alloc_size; //8byte
size_t max_alloc_size; //64k
size_t max_mem; //最大内存
void * __alloc_mem; //申请总内存
void * __extra_mem; //分配好总内存后剩余的内存
size_t use_mem; //已经使用总内存
};
#include "ZSMemoryPool.h"
#include
ZSMemoryPool::ZSMemoryPool()
{
}
ZSMemoryPool::ZSMemoryPool(const ZSMemoryPool&)
{
}
ZSMemoryPool::ZSMemoryPool(size_t min_alloc_size, size_t max_alloc_size, size_t max_mem)
{
this->min_alloc_size = min_alloc_size + EXTRA_MEM;
this->max_alloc_size = max_alloc_size + EXTRA_MEM;
this->max_mem = max_mem;
trunks = nullptr;
blocks = nullptr;
__alloc_mem = nullptr;
__extra_mem = nullptr;
ZSPoolInit();
}
ZSMemoryPool::~ZSMemoryPool()
{
ZSDestroy();
}
int ZSMemoryPool::ZSPoolInit()
{
size_t n = min_alloc_size;
size_t m = max_alloc_size;
size_t k = max_mem;
for (size_t i = 1;; i <<= 1)//矫正最大值m,矫正为m<=2^i
{
if (m <= i)
{
m = i;
max_alloc_size = i;
break;
}
}
for (size_t i = m;; i >>= 1)//矫正最小值n,矫正为n>=2^i
{
if (n >= i)
{
n = i<<1;
min_alloc_size = n;
break;
}
}
for (size_t i = k;; i <<= 1)//矫正总内存k,矫正为k<=2^i
{
if (k <= i)
{
k = i;
max_mem = k;
break;
}
}
ZSPoolMemInit(n, m, k);
return 0;
}
void * ZSMemoryPool::ZSAlloc(size_t alloc_size)
{
//匹配最佳位置
int idx = 0;
size_t _fit_mem = min_alloc_size;
while (_fit_mem < alloc_size || blocks[idx].free_cnt <= 0)
{
_fit_mem <<= 1;
++idx;
}
if (idx >= _distance)
{
printf("内存池已满,直接分配内存\n");
return new char[alloc_size]();
}
for (int i = 0; i < bk_cnt; ++i)
{
MBlock_t * rtBk = (MBlock_t*)blocks[idx].start_adr + i*_fit_mem;
if (rtBk->isUsed = MEM_IS_FREE)
{
rtBk->isUsed = MEM_IS_USED;
blocks[idx].use_cnt += 1;
blocks[idx].free_cnt -= 1;
use_mem += rtBk->iLength;
return rtBk->pBuffer;
}
}
return nullptr;
}
int ZSMemoryPool::ZSFree(void * _freePtr)
{
if (_freePtr != nullptr)
{
MBlock_t *check = (MBlock_t *)((char*)_freePtr - EXTRA_MEM);
if (check->isUsed == MEM_IS_USED)
{
check->isUsed = MEM_IS_FREE;
MBlock *bk = (MBlock*)check->pHead;
bk->free_cnt += 1;
bk->use_cnt -= 1;
use_mem -= check->iLength;
return 0;
}
else
{
printf("该内存不是由此内存池分配,无法处理");
return 1;
}
}
return -1;
}
int ZSMemoryPool::ZSDestroy()
{
MChunk *tmp = trunks;
MChunk *pPre = trunks;
while (pPre != nullptr)
{
tmp = pPre->pNext;
if (pPre->buf != nullptr)
delete[] (char*)pPre->buf;
delete pPre;
pPre = tmp;
}
return 0;
}
int ZSMemoryPool::add_head(MChunk * ck)
{
if (ck != nullptr)
{
ck->pNext = trunks;
trunks = ck;
return 0;
}
return -1;
}
int ZSMemoryPool::ZSPoolMemInit(size_t _Ssize, size_t _Esize, size_t _Tsize)
{
if (__alloc_mem == nullptr)
__alloc_mem = new char[max_mem]();
//计算每个slab中的buf分配个数
int alloc_count = _Tsize / (_Esize << 1);
bk_cnt = alloc_count;
int offset = 0;//内存块偏移量
size_t st = _Ssize;
size_t ed = _Esize;
int dst = 0;
while (st <= ed)
{
st <<= 1;
++dst;
}
_distance = dst;//计算最大最小内存块距离
--dst;
blocks = new MBlock [_distance]();
for (size_t i = _Esize; i >= _Ssize; i >>= 1)
{
MChunk *ck = new MChunk();
ck->size = i;
ck->buf = (char*)__alloc_mem + offset;
for (int j = 0; j < alloc_count; ++j)
{
MBlock_t *_tmp_block = (MBlock_t *)((char*)__alloc_mem + offset + j*ck->size);
_tmp_block->iLength = ck->size;
_tmp_block->isUsed = MEM_IS_FREE;
_tmp_block->pBuffer = MEM_OFFSER(_tmp_block);
if (j == 0)
{
blocks[dst].free_cnt = bk_cnt;
blocks[dst].use_cnt = 0;
blocks[dst].start_adr = (char*)_tmp_block;
}
_tmp_block->pHead = (void*)&blocks[dst];
}
dst--;
add_head(ck);
offset += i*alloc_count;
}
return 0;
}