C語言實現記憶體池
阿新 • • 發佈:2019-02-17
之前編寫資料管理結構時用到記憶體池,在寫過的記憶體管理結構(記憶體管理結構)的基礎上進行重新設計,使其達到記憶體釋放"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"));
程式碼下載地址:記憶體池下載!