1. 程式人生 > >C語言實現記憶體池

C語言實現記憶體池

之前編寫資料管理結構時用到記憶體池,在寫過的記憶體管理結構(記憶體管理結構)的基礎上進行重新設計,使其達到記憶體釋放"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"));

程式碼下載地址:記憶體池下載