绑定对象的无锁内存池

2 篇文章 0 订阅

        最近,一直想写自己的内存池,也参考了网上很多的内存池例子,就写了自己的第一个内存池,因为网上大部分的内存池都是全局内存池,因为公司的项目中每个单位是房间,那么我就在想能不能写一个,适合自己项目的一个分配快捷,又不会产生过多内存碎片的内存池。于是,我自己写了一个内存池,新手代码,请勿介意!

下面是我自己写的内存池大概结构:



由于这个结构在网上已经有很多更加详细的描述,那么我就在这里简单的介绍一下各个模块的功能:

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 <stdio.h>

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;
}


因为以前觉得自己写的内存池是类似于SLAB算法分配策略的内存池,但是又觉得不像,所以在代码内部有部分的注释可能是错误的概念。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值