C语言实现内存池

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a29562268/article/details/82846305

之前编写数据管理结构时用到内存池,在写过的内存管理结构(内存管理结构)的基础上进行重新设计,使其达到内存释放"0碎片"、一体化管理。

目前内存池拥有以下功能:
1.自适应分配超过默认内存池大小的单个内存池(单个内存池默认大小为100M,可以通过修改宏定义或调用修改函数指定新的内存池大小;单个内存池最大内存分配与编译器分配相同,MFC32位工程最大分配在1G左右);

2.每个内存池都有自己的内存池描述信息,每当分配一个新的内存池时,内存0-52字节为内存池的描述信息(这个由内存池动态创建,会随着不同编辑器位数而改变);

3.单个内存池只需要管理自己内部的数据链表结构和数据存储,数据链表节点直接从内存池分配,这样可以保证内存池的连续分配以及随机释放指定内存,避免内存碎片的产生;

4.用户内存释放时,内存池根据内部的数据链表结构自动合并相邻未使用的内存(未使用的内存是由链表节点管理的由用户使用过(不再使用)的内存)以及擦除掉不需要的数据链表节点;达到放回内存池条件的内存,去掉链表节点后将由内存池管理;

5.内存池大于等于两个或者更多的情况下,如果内存使用率不超过内存总量的百分之四十,将会自动释放当前未使用的内存池;

6.对象析构的时候会释放掉所有的内存池(所有内存全部由内存池分配,释放时只需要释放内存池即可)。

下面介绍内存池设计方式:

//内存管理表
//允许使用者追加分配内存,追加的内存将会保存在下一个结构中
typedef struct MemoryStore
{
	int Count;
	//总容量
	unsigned long MemVolumeDose;
	//起始地址
	unsigned long StartAddress;
	//末尾地址
	unsigned long EndAddress;
	//当前使用量
	unsigned long CurrentUsageAmount;
	//剩余容量
	unsigned long SurplusVolumeDose;
	MemoryStore *Priv, *Next;
	//存储数据起始地址
	unsigned long StartDataAddress;
	//当前存储数据偏移地址
	unsigned long StartDataOffsetAddress;
	//内存管理表大小 用来计算内存的使用情况
	unsigned long MemoryStoreSize;
	//数据管理
	PMemList pMemHead,pMemEnd;
	void Init()
	{
		Count = MemVolumeDose = StartAddress = EndAddress = CurrentUsageAmount = SurplusVolumeDose = StartDataAddress = StartDataOffsetAddress = 0;
		Priv = Next = 0;
		//内存管理表大小多分配一个字节,防止内存管理表信息与后面的数据空间连续
		MemoryStoreSize = (sizeof(MemoryStore) + 1);
		//每个内存池都有自己的数据管理表,这样方便内存回收
		pMemHead = pMemEnd = 0;
	}
	//获取节点头
	PMemList GetHeadList()
	{
		return pMemHead;
	}

	//获取最后一个节点
	PMemList GetEndList()
	{
		return pMemEnd;
	}

}* PMemoryStore;
//标记链表节点在内存池中的信息
struct MemNode
{
	//起始地址
	unsigned long StartAddress;
	//当前使用量
	unsigned long CurrentUsgeAmount;
};

//链表管理结构
typedef struct MemList
{
	//起始地址
	unsigned long StartAddress;
	//末尾地址
	unsigned long EndAddress;
	//当前使用量
	unsigned long CurrentUsgeAmount;
	//标记是否已经保存了数据
	bool bValid;
	MemList *Priv, *Next;
	//当前链表节点在内存池中的信息
	MemNode nodeInfo;
	void Init()
	{
		StartAddress = EndAddress = CurrentUsgeAmount = 0;
		bValid = 0;
		Priv = Next = 0;
	}
}* PMemList;
//记录内存池使用率
struct PoolCalculate
{
	//内存池总容量
	unsigned long long PoolTotal;
	//当前内存池使用量
	unsigned long long PoolUsage;
	//内存池数量
	int PoolAmount;

	PoolCalculate():PoolTotal(0),PoolUsage(0),PoolAmount(0)
	{
	}
};
//内存池创建
//多分配出内存池结构描述表内存和1个数据链表节点,这样可以保证一次分配出超出内存池大小的内存
		char *Mem = (char *)malloc(AllocSize + sizeof(MemoryStore) + 1 + sizeof(MemList) + 1);
		if (0 == Mem)
			return false;

		if (0 == m_Memory)
		{
			m_Memory = (PMemoryStore)Mem;
			m_Memory->Init();
		}
		m_Memory->StartAddress = (unsigned long)Mem;
		m_Memory->EndAddress = (m_Memory->StartAddress + AllocSize + m_Memory->MemoryStoreSize + sizeof(MemList) + 1);
		m_Memory->MemVolumeDose = (AllocSize + m_Memory->MemoryStoreSize + sizeof(MemList) + 1);
//数据链表节点创建
list = (PMemList)(pool->StartDataAddress + pool->StartDataOffsetAddress);
		list->Init();
		list->nodeInfo.StartAddress = (pool->StartDataAddress + pool->StartDataOffsetAddress);
		list->nodeInfo.CurrentUsgeAmount = (sizeof(MemList) + 1);
		pool->pMemHead = list;

		pool->CurrentUsageAmount += list->nodeInfo.CurrentUsgeAmount;
		pool->SurplusVolumeDose -= list->nodeInfo.CurrentUsgeAmount;
		//当前数据偏移地址
		pool->StartDataOffsetAddress += list->nodeInfo.CurrentUsgeAmount;

		m_PoolCal.PoolUsage += list->nodeInfo.CurrentUsgeAmount;
//数据分配
list->StartAddress = (obj->StartDataAddress + obj->StartDataOffsetAddress);
	//多分配一个字节用来防止内存越界
	list->EndAddress = (list->StartAddress + nSize + 1);
	list->CurrentUsgeAmount = (nSize + 1);
	list->bValid = bValid;
	obj->CurrentUsageAmount += list->CurrentUsgeAmount;
	obj->SurplusVolumeDose -= list->CurrentUsgeAmount;
	obj->StartDataOffsetAddress += list->CurrentUsgeAmount;
	obj->pMemEnd = list;
	obj->pMemEnd->Next = 0;

	m_PoolCal.PoolUsage += list->CurrentUsgeAmount;
	//分配出一段干净的内存 上层方便使用
	memset((void *)list->StartAddress,0,list->CurrentUsgeAmount);
//用户释放数据合并方式 这里贴上向左合并
int nNum = 0;
	while (list->Priv && !list->Priv->bValid)
	{
		list->Priv->CurrentUsgeAmount += (list->nodeInfo.CurrentUsgeAmount + list->CurrentUsgeAmount);
		list->Priv->EndAddress = list->EndAddress;
		//如果是第一次合并内存,只需要减去链表描述结构大小即可
		if (0 == nNum)
		{
			pool->CurrentUsageAmount -= list->nodeInfo.CurrentUsgeAmount;
			pool->SurplusVolumeDose += list->nodeInfo.CurrentUsgeAmount;

			m_PoolCal.PoolUsage -= list->nodeInfo.CurrentUsgeAmount;
		}
		else
		{
			pool->CurrentUsageAmount -= (list->nodeInfo.CurrentUsgeAmount + list->CurrentUsgeAmount);
			pool->SurplusVolumeDose += (list->nodeInfo.CurrentUsgeAmount + list->CurrentUsgeAmount);

			m_PoolCal.PoolUsage -= (list->nodeInfo.CurrentUsgeAmount + list->CurrentUsgeAmount);
		}
		++nNum;

		if (list->Next)
		{
			list->Priv->Next = list->Next;
			list->Next->Priv = list->Priv;
		}
		else
		{
			list->Priv->Next = 0;
		}

		list = list->Priv;
		//如果上一个节点存在 而且是有效节点 或者 上一个节点不存在,直接返回本次执行结果
		if ((list->Priv  && list->Priv->bValid) || !list->Priv)
			return list;
	}
	return list;
//内存放入内存池
if (list && !list->Next)
	{
		pool->CurrentUsageAmount -= list->nodeInfo.CurrentUsgeAmount;
		pool->SurplusVolumeDose += list->nodeInfo.CurrentUsgeAmount;
		pool->StartDataOffsetAddress = (list->nodeInfo.StartAddress - pool->StartDataAddress);

		m_PoolCal.PoolUsage -= list->nodeInfo.CurrentUsgeAmount;

		if (list->Priv)
			list->Priv->Next = 0;
		else //数据链表已经没有节点了,完全由内存池分配使用
			pool->pMemHead = pool->pMemEnd = 0;
	}
//内存使用率小于百分之四十释放多余的内存池
ReRun:
	if (40 >= ((double)m_PoolCal.PoolUsage / (double)m_PoolCal.PoolTotal * 100) && 2 <= m_PoolCal.PoolAmount)
	{
		//找到未使用的内存池进行释放
		PMemoryStore memPool = GetPoolHead();
		while (0 != memPool)
		{
			if (0 == memPool->StartDataOffsetAddress)
			{
				MemPoolFree_(memPool->Count);
				//释放内存池后,继续检查内存使用率是否大于百分之四十
				goto ReRun;
			}
			else
				memPool = memPool->Next;
		}
	}

接口函数说明:

//内存分配
	void *Lm_MemAlloc(unsigned long nSize);
	//内存释放
	bool Lm_MemFree(void * ptr);
	//释放内存池 类对象销毁前进行自动释放
	bool Lm_MemPoolFree();
	char *Lm_GetLastError();
	//匹配的内存比需要分配的内存多的字节数(最小值 比如默认匹配到的内存比需要分配的内存多一个字节)
	bool Lm_SetComPareMemMini(int nMini);
	//设置单个内存池大小
	bool Lm_SetPoolSize(unsigned long nSize);

测试示例:

Lm_MemoryPool pool;
	char *Test1 = (char *)pool.Lm_MemAlloc(100);
	memcpy(Test1,"您好12312321312",sizeof("您好12312321312"));
	memset(Test1,0,sizeof(Test1));
	char *Test2 = (char *)pool.Lm_MemAlloc(100);
	memcpy(Test2,"您好12312321312",sizeof("您好12312321312"));
	memset(Test2,0,sizeof(Test2));
	char *Test3 = (char *)pool.Lm_MemAlloc(1024 * 1024 * 1024);
	memcpy(Test3,"您好12312321312",sizeof("您好12312321312"));
	memset(Test3,0,sizeof(Test3));
	char *Test4 = (char *)pool.Lm_MemAlloc(100 * 1024);
	memcpy(Test4,"您好12312321312",sizeof("您好12312321312"));

	pool.Lm_MemFree(Test1);
	Test1 = 0;
	pool.Lm_MemFree(Test2);
	Test2 = 0;
	pool.Lm_MemFree(Test3);
	Test3 = 0;
	pool.Lm_MemFree(Test4);
	Test4 = 0;
	pool.Lm_MemPoolFree();

	Test1 = (char *)pool.Lm_MemAlloc(100);
	memcpy(Test1,"您好12312321312",sizeof("您好12312321312"));
	memset(Test1,0,sizeof(Test1));
	Test2 = (char *)pool.Lm_MemAlloc(100);
	memcpy(Test2,"您好12312321312",sizeof("您好12312321312"));
	memset(Test2,0,sizeof(Test2));
	Test3 = (char *)pool.Lm_MemAlloc(100 * 1024 * 1024);
	memcpy(Test3,"您好12312321312",sizeof("您好12312321312"));
	memset(Test3,0,sizeof(Test3));
	Test4 = (char *)pool.Lm_MemAlloc(100 * 1024);
	memcpy(Test4,"您好12312321312",sizeof("您好12312321312"));

	pool.Lm_MemPoolFree();

	Test1 = (char *)pool.Lm_MemAlloc(100);
	memcpy(Test1,"您好12312321312",sizeof("您好12312321312"));
	memset(Test1,0,sizeof(Test1));
	Test2 = (char *)pool.Lm_MemAlloc(100);
	memcpy(Test2,"您好12312321312",sizeof("您好12312321312"));
	memset(Test2,0,sizeof(Test2));
	Test3 = (char *)pool.Lm_MemAlloc(100 * 1024 * 1024);
	memcpy(Test3,"您好12312321312",sizeof("您好12312321312"));
	memset(Test3,0,sizeof(Test3));
	Test4 = (char *)pool.Lm_MemAlloc(100 * 1024);
	memcpy(Test4,"您好12312321312",sizeof("您好12312321312"));

代码下载地址:内存池下载

猜你喜欢

转载自blog.csdn.net/a29562268/article/details/82846305