C語言實現簡單的記憶體管理機制
在C型別程式中,棧記憶體比較珍貴,大部分用在區域性或者類成員(因為稀少… 不適合長時間佔用一塊棧記憶體),對於大量資料一般使用堆來分配。重複用堆分配有一個顯著的缺點就是容易造成外部碎片,在這種情況下系統記憶體不再連貫,一些記憶體得不到使用,久而久之系統記憶體會變的越來越少,長時間處理大資料會出現一些不可預料的問題。
針對這種情況,我以自己的習慣寫了一個簡單的記憶體管理結構,加深自己對記憶體的理解。
首先簡單說下我實驗的結構形式:
1.預設分配一塊大的記憶體區域(類似記憶體池,預設大小是100M,構造的時候可以自己指定區域的大小),後期根據需要的記憶體動態增加新的記憶體區域(預設大小也是100M,如果需要使用的記憶體超過100M將會分配需要使用的記憶體加上最小範圍值),以連結串列的形式進行連結;
2.使用連結串列結構標記已使用和已歸還的記憶體範圍;
3.每次獲取記憶體時先從已歸還的記憶體結構中進行匹配,如果沒有匹配到合適的記憶體大小,將從記憶體區域中劃分出一塊新的記憶體使用;
4.增加每段記憶體的保護位元組:1.最小範圍值 預設為1個位元組,2.最大範圍值 預設為2個位元組。每次獲取記憶體時首先根據 ( 大於等於需要的記憶體加最小範圍值 或 <= 需要的記憶體加最大範圍值 )進行匹配已歸還的記憶體結構;
5.記憶體區域以及記憶體連結串列結構都是使用計數表示在相同型別中的指定位置;
6.清除指定記憶體區域(記憶體池)時,把標記使用這塊記憶體的連結串列記憶體置為空,等待下次分配使用;
7.連結串列記憶體結構只會在物件析構的時候進行釋放,保證了記憶體分配速度越來越快並且穩定的情況(不在物件使用的時候進行刪除連結串列記憶體節點,保證了記憶體分配的效率);
8.物件析構的時候會釋放掉所有的記憶體區域(記憶體池)以及連結串列記憶體結構,保證了結構的穩定性。
下面貼上.h檔案:
#include <stdio.h>
//預設單個記憶體池大小
#define MEMPOOLSIZE 100 * 1024 * 1024
//記憶體管理表
//允許使用者追加分配記憶體,追加的記憶體將會儲存在下一個結構中
typedef struct MemoryStore
{
int Count;
//總容量
unsigned long long MemVolumeDose;
//起始地址
unsigned long long StartAddress;
//末尾地址
unsigned long long EndAddress;
//當前使用量
unsigned long long CurrentUsageAmount;
//剩餘容量
unsigned long SurplusVolumeDose;
MemoryStore *Priv, *Next;
void Init()
{
Count = MemVolumeDose = StartAddress = EndAddress = CurrentUsageAmount = SurplusVolumeDose = 0;
Priv = Next = 0;
}
}* PMemoryStore;
//連結串列記憶體管理結構
typedef struct MemList
{
//記錄是第多少個節點
int Count;
//記錄記憶體屬於哪個池子
int nPoolCount;
//起始地址
unsigned long StartAddress;
//末尾地址
unsigned long EndAddress;
//當前使用量
unsigned long CurrentUsgeAmount;
//標記是否已經儲存了資料
bool bValid;
MemList *Priv, *Next;
void Init()
{
Count = nPoolCount = StartAddress = EndAddress = CurrentUsgeAmount = 0;
bValid = 0;
Priv = Next = 0;
}
}* PMemList;
//程式碼實現結構
struct MemoryPool_
{
MemoryPool_(unsigned long long AllocSize = MEMPOOLSIZE);
virtual ~MemoryPool_();
//記憶體分配
void *MemAlloc(int nSize);
//記憶體釋放
bool MemFree(void * ptr);
//釋放記憶體池 類物件銷燬前進行自動釋放
bool MemPoolFree();
//獲取最後一次出現的錯誤
char *GetLastError();
//匹配的記憶體比需要分配的記憶體多的位元組數(最小值 比如預設匹配到的記憶體比需要分配的記憶體多一個位元組)
bool SetComPareMemMini(int nMini);
//匹配的記憶體比需要分配的記憶體多的位元組數(最大值 比如預設匹配到的記憶體比需要分配的記憶體多五個位元組)
bool SetComPareMemMax(int nMax);
private://內部使用
//記憶體池管理
PMemoryStore m_Memory,m_MemoryEnd;
//資料管理
PMemList m_MemHead,m_MemCurrent,m_MemEnd;
char m_LastError[256];
//匹配最小值差距
int m_nCompareMini;
//匹配最大值差距
int m_nCompareMax;
//初始化記憶體池大小
unsigned long long m_nInitPoolSize;
//標記被解除佔用的一節
int m_nCount;
private:
//執行中最後出現的錯誤進行儲存
bool WriteLastError(const char *data);
//初始化記憶體池
bool InitMemPool(int AllocSize);
//建立下一個記憶體池
bool CreateNextMemPool(int AllocSize);
//分配一節記憶體管理連結串列資訊
PMemList GetMemList();
//初始化記憶體管理連結串列頭資訊
bool InitMemHead();
//獲取記憶體管理管理頭資訊
PMemList GetMemListHead();
//從記憶體池中分配一塊記憶體
void *GetPoolMem(int nSize);
//獲取首個記憶體池資訊
PMemoryStore GetPoolHead();
//獲取最後一個記憶體池資訊
PMemoryStore GetPoolEnd();
//從一塊記憶體池中獲取資料
void *GetAPoolofMem(PMemoryStore obj,int nSize);
//獲取最後一個節點
PMemList GetEndList();
//建立第一個節點以及分配記憶體
void *CreateFirstMem(int nSize,bool bValid);
//建立新的節點以及分配記憶體 bValid為true標記為已使用記憶體 ,false標記為未使用
void *CreateNextMem(PMemoryStore obj,int nSize,bool bValid);
//建立第一個節點
void *CreateListHead(int nSize);
//獲取節點頭
PMemList GetHeadList();
//釋放指定編號記憶體池
bool MemPoolFree_(int nCount);
//修改指定記憶體塊當前使用量
bool RevampMemUsgeAmount(int nCount,long long nSize);
//解除佔用標記的某塊記憶體 ,釋放類物件前統一釋放
bool MemListFree_(int nPoolCount);
//標記第一個解除佔用的記憶體
bool SetAllocList(int Count);
//獲取第一個被解除佔用的記憶體節點
int GetAllocList();
//使用被解除佔用的節點分配記憶體
void *AllocMemList(int nCount,int nSize,bool bValid);
//根據計數獲取list節點
PMemList GetCountList(int Count);
//獲取可以分出記憶體的池子,這個直接從空閒記憶體中取出需要的記憶體
PMemoryStore GetValidMemPool(int nSize);
//釋放指定節點,返回下一個節點
PMemList FreeList_(int Count);
//釋放連結串列的所有節點
bool FreeList();
};
下面貼上.cpp檔案:
#include "stdafx.h"
#include "TestMemoryPool.h"
MemoryPool_::MemoryPool_(unsigned long long AllocSize):m_Memory(0),m_MemHead(0)
,m_MemCurrent(0),m_MemEnd(0),m_nInitPoolSize(AllocSize)
{
m_nCompareMini = 1;
m_nCompareMax = 5;
memset(m_LastError,0,sizeof(m_LastError));
}
MemoryPool_::~MemoryPool_()
{
MemPoolFree();
FreeList();
}
bool MemoryPool_::SetComPareMemMini(int nMini)
{
if (1 >= nMini)
{
return false;
}
m_nCompareMini = nMini;
return true;
}
bool MemoryPool_::SetComPareMemMax(int nMax)
{
if (1 >= nMax)
{
return false;
}
m_nCompareMax = nMax;
return true;
}
//獲取執行中出現的最後一條錯誤
char *MemoryPool_::GetLastError()
{
return m_LastError;
}
//寫入錯誤資訊
bool MemoryPool_::WriteLastError(const char *data)
{
if (0 == data)
return false;
memset(m_LastError,0,sizeof(m_LastError));
memcpy(m_LastError,data,sizeof(data));
return true;
}
//初始化記憶體池
bool MemoryPool_::InitMemPool(int AllocSize)
{
if (0 == m_Memory)
{
m_Memory = (PMemoryStore)malloc(sizeof(MemoryStore));
m_Memory->Init();
}
if (0 == m_Memory)
return false;
//構建池子
if (0 < AllocSize)
{
m_Memory->MemVolumeDose = AllocSize;
char *Mem = (char *)malloc(AllocSize);
m_Memory->StartAddress = (unsigned long long)Mem;
m_Memory->EndAddress = (m_Memory->StartAddress + AllocSize);
}
else
{
m_Memory->MemVolumeDose = MEMPOOLSIZE;
char *Mem = (char *)malloc(MEMPOOLSIZE);
m_Memory->StartAddress = (unsigned long long)Mem;
m_Memory->EndAddress = (m_Memory->StartAddress + MEMPOOLSIZE);
}
m_Memory->Count = 1;
m_Memory->CurrentUsageAmount = 0;
m_Memory->SurplusVolumeDose = m_Memory->MemVolumeDose;
//分配記憶體失敗
if (0 == m_Memory->StartAddress)
{
WriteLastError("this MemoryAlloc is Not Valid");
return false;
}
m_MemoryEnd = m_Memory;
return true;
}
//建立下一個記憶體池
bool MemoryPool_::CreateNextMemPool(int AllocSize)
{
PMemoryStore memoryPool = GetPoolHead();
if (0 == memoryPool)
{
InitMemPool(((AllocSize + m_nCompareMini >= MEMPOOLSIZE) ? (AllocSize + m_nCompareMini) : MEMPOOLSIZE));
return true;
}
while (memoryPool && 0 != memoryPool->Next)
memoryPool = memoryPool->Next;
memoryPool->Next = (PMemoryStore)malloc(sizeof(MemoryStore));
memoryPool->Next->Init();
//構建池子
if (0 < AllocSize)
{
memoryPool->Next->MemVolumeDose = AllocSize;
char *Mem = (char *)malloc(AllocSize);
memoryPool->Next->StartAddress = (unsigned long long)Mem;
memoryPool->Next->EndAddress = (memoryPool->Next->StartAddress + AllocSize);
}
else
{
memoryPool->Next->MemVolumeDose = MEMPOOLSIZE;
char *Mem = (char *)malloc(MEMPOOLSIZE);
memoryPool->Next->StartAddress = (unsigned long long)Mem;
memoryPool->Next->EndAddress = (memoryPool->Next->StartAddress + MEMPOOLSIZE);
}
memoryPool->Next->Count = (memoryPool->Count + 1);
memoryPool->Next->CurrentUsageAmount = 0;
memoryPool->Next->SurplusVolumeDose = memoryPool->Next->MemVolumeDose;
//分配記憶體失敗
if (0 == memoryPool->Next->StartAddress)
{
WriteLastError("this MemoryAlloc is Not Valid");
return false;
}
m_MemoryEnd = memoryPool->Next;
m_MemoryEnd->Priv = memoryPool;
return true;
}
//記憶體分配
void *MemoryPool_::MemAlloc(int nSize)
{
//增加頭節點
if (0 == GetMemListHead())
{
if (!InitMemPool(m_nInitPoolSize))
{
WriteLastError("this Init is Not Valid");
return 0;
}
return CreateListHead(nSize);
}
else
{
return GetPoolMem(nSize);
}
}
//建立第一個節點
void *MemoryPool_::CreateListHead(int nSize)
{
return CreateFirstMem(nSize,1);
}
//獲取節點頭
PMemList MemoryPool_::GetHeadList()
{
return m_MemHead;
}
//獲取首個記憶體池資訊
PMemoryStore MemoryPool_::GetPoolHead()
{
return m_Memory;
}
//獲取最後一個記憶體池資訊
PMemoryStore MemoryPool_::GetPoolEnd()
{
return m_MemoryEnd;
}
//從所有記憶體池中取出未使用的記憶體地址
void *MemoryPool_::GetPoolMem(int nSize)
{
PMemoryStore pool = GetPoolHead();
while (pool)
{
char *pData = (char *)GetAPoolofMem(pool,nSize);
if (0 != pData)
return (void *)pData;
pool = pool->Next;
}
//如果所有的池子都遍歷了還是沒有合適記憶體,那麼建立一個新的池子
if ((nSize + m_nCompareMini) > MEMPOOLSIZE)
CreateNextMemPool(nSize + m_nCompareMini);
else
CreateNextMemPool(MEMPOOLSIZE);
char *pData = (char *)GetAPoolofMem(m_MemoryEnd,nSize);
return (void *)pData;
}
//從一塊記憶體池中獲取資料
void *MemoryPool_::GetAPoolofMem(PMemoryStore obj,int nSize)
{
if (0 >= obj->SurplusVolumeDose || nSize >= obj->SurplusVolumeDose)
{
return 0;
}
//如果達到查詢的條件 開始遍歷對應編號的記憶體池 ,為了最大利用記憶體選擇從頭開始遍歷,如果沒有資料插入到最後
PMemList listData = GetMemListHead();
while (listData && (0 != listData->Next))
{
//判斷是否有解除佔用的節點,如果有的話記錄第一個,如果沒有找到合適的記憶體鏈 ,那麼用這個進行分配記憶體
if ((listData->nPoolCount == 0) && (0 < listData->Count))
{
SetAllocList(listData->Count);
}
//如果節點中儲存的記憶體使用量大於或等於需要分配的記憶體,那麼使用這塊記憶體
if (((nSize + m_nCompareMini <= listData->CurrentUsgeAmount) && (nSize + m_nCompareMax >= listData->CurrentUsgeAmount)) && (0 == listData->bValid))
{
RevampMemUsgeAmount(listData->nPoolCount,listData->CurrentUsgeAmount);
listData->bValid = 1;
return (void *)listData->StartAddress;
}
listData = listData->Next;
}
int nCount = GetAllocList();
if (0 < nCount)
return AllocMemList(nCount,nSize,1);
//建立新的節點儲存分配記憶體
return CreateNextMem(obj,nSize,1);
}
//標記第一個被解除佔用的記憶體節點
bool MemoryPool_::SetAllocList(int Count)
{
if (0 >= Count || 0 < m_nCount)
return false;
m_nCount = Count;
return true;
}
//獲取第一個被解除佔用的記憶體節點,當前儲存的節點被使用後可以再次儲存下一個被解除佔用的記憶體節點
int MemoryPool_::GetAllocList()
{
int Count = m_nCount;
m_nCount = 0;
return Count;
}
//修改指定記憶體塊當前使用量
bool MemoryPool_::RevampMemUsgeAmount(int nCount,long long nSize)
{
if (0 >= nCount)
return false;
PMemoryStore memPool = GetPoolHead();
while (memPool->Count != nCount)
memPool = memPool->Next;
if (0 != memPool)
{
memPool->CurrentUsageAmount += nSize;
memPool->SurplusVolumeDose = (memPool->MemVolumeDose - memPool->CurrentUsageAmount);
}
else
return false;
return true;
}
//建立第一個節點以及分配記憶體 ,如果不是第一個節點 走預設函式
void *MemoryPool_::CreateFirstMem(int nSize,bool bValid)
{
//如果頭節點已經建立 呼叫預設記憶體分配
if (0 != m_MemHead)
return GetPoolMem(nSize);
PMemoryStore pool = GetPoolHead();
m_MemHead = GetMemList();
m_MemHead->Count = 1;
m_MemHead->StartAddress = (pool->StartAddress + pool->CurrentUsageAmount);
//多分配一個位元組用來防止記憶體越界
m_MemHead->EndAddress = (m_MemHead->StartAddress + nSize + 1);
m_MemHead->CurrentUsgeAmount = (nSize + 1);
m_MemHead->nPoolCount = pool->Count;
m_MemHead->bValid = bValid;
pool->CurrentUsageAmount += (nSize + 1);
pool->SurplusVolumeDose -= pool->CurrentUsageAmount;
m_MemEnd = m_MemHead;
//分配出一段乾淨的記憶體 上層方便使用
memset((void *)m_MemHead->StartAddress,0,nSize + 1);
return (void *)m_MemHead->StartAddress;
}
//建立新的節點以及分配記憶體
void *MemoryPool_::CreateNextMem(PMemoryStore obj,int nSize,bool bValid)
{
PMemList list = GetEndList();
list->Next = GetMemList();
list->Next->Count = (list->Count + 1);
list->Next->StartAddress = (obj->StartAddress + obj->CurrentUsageAmount);
//多分配一個位元組用來防止記憶體越界
list->Next->EndAddress = (list->Next->StartAddress + nSize + 1);
list->Next->CurrentUsgeAmount = (nSize + 1);
list->Next->nPoolCount = obj->Count;
list->Next->Priv = list;
list->Next->bValid = bValid;
obj->CurrentUsageAmount += (nSize + 1);
obj->SurplusVolumeDose -= obj->CurrentUsageAmount;
m_MemEnd = list->Next;
//分配出一段乾淨的記憶體 上層方便使用
memset((void *)list->Next->StartAddress,0,nSize + 1);
return (void *)list->Next->StartAddress;
}
//獲取可以分出記憶體的池子,這個直接從空閒記憶體中取出需要的記憶體
PMemoryStore MemoryPool_::GetValidMemPool(int nSize)
{
PMemoryStore pool = GetPoolHead();
while (pool)
{
if (pool->SurplusVolumeDose >= (nSize + m_nCompareMini))
{
return pool;
}
pool = pool->Next;
}
//如果沒有 就建立一個新的記憶體池
if (CreateNextMemPool(((nSize + m_nCompareMini) >= MEMPOOLSIZE ? (nSize + m_nCompareMini) : MEMPOOLSIZE)))
return GetPoolEnd();
return 0;
}
//使用被解除佔用的節點分配記憶體
void *MemoryPool_::AllocMemList(int nCount,int nSize,bool bValid)
{
PMemList list = GetCountList(nCount);
if (0 == list)
return 0;
PMemoryStore memPool = GetValidMemPool(nSize);
if (0 == memPool)
return 0;
list->StartAddress = (memPool->StartAddress + memPool->CurrentUsageAmount);
//多分配一個位元組用來防止記憶體越界
list->EndAddress = (list->StartAddress + nSize + 1);
list->CurrentUsgeAmount = (nSize + 1);
list->nPoolCount = memPool->Count;
list->bValid = bValid;
memPool->CurrentUsageAmount += (nSize + 1);
memPool->SurplusVolumeDose = (memPool->MemVolumeDose - memPool->CurrentUsageAmount);
//分配出一段乾淨的記憶體 方便使用
memset((void *)list->StartAddress,0,nSize + 1);
return (void *)list->StartAddress;
}
//根據計數獲取list節點
PMemList MemoryPool_::GetCountList(int Count)
{
if (0 < Count)
{
PMemList list = GetHeadList();
while (list)
{
if (list->Count == Count)
{
return list;
}
list = list->Next;
}
}
return 0;
}
//獲取最後一個節點
PMemList MemoryPool_::GetEndList()
{
return m_MemEnd;
}
//獲取連結串列記憶體結構頭節點
PMemList MemoryPool_::GetMemListHead()
{
return m_MemHead;
}
//建立連結串列記憶體結構頭
bool MemoryPool_::InitMemHead()
{
m_MemHead = GetMemList();
m_MemCurrent = m_MemEnd = m_MemHead;
return (m_MemHead != 0 ? true : false);
}
//建立連結結構節點
PMemList MemoryPool_::GetMemList()
{
PMemList list = (PMemList)malloc(sizeof(MemList));
list->Init();
return list;
}
//記憶體釋放
bool MemoryPool_::MemFree(void * ptr)
{
//根據分配的地址在記憶體池中匹配,匹配到後 修改結構資料後,等待再次使用
PMemList list = GetMemListHead();
while (list)
{
//如果連結串列中其中一節資料與需要釋放的地址相同 ,而且這段資料屬於使用中,屬於這塊記憶體
if ((list->StartAddress == (unsigned long)ptr) && (1 == list->bValid))
{
RevampMemUsgeAmount(list->nPoolCount,~(long long)list->CurrentUsgeAmount + 1);
//回收的時候不需要初始化記憶體,因為再次使用的時候會進行初始化
list->bValid = 0;
ptr = 0;
return true;
}
list = list->Next;
}
return false;
}
//釋放記憶體池 ,這個不需要手動釋放 ,類物件銷燬前會進行釋放
bool MemoryPool_::MemPoolFree()
{
PMemoryStore memPool = GetPoolHead();
while (0 != memPool)
{
PMemoryStore next = memPool->Next;
MemPoolFree_(memPool->Count);
memPool = next;
}
return true;
}
//釋放指定編號記憶體池
bool MemoryPool_::MemPoolFree_(int nCount)
{
PMemoryStore memPool = GetPoolHead();
while (memPool->Count != nCount)
memPool = memPool->Next;
if (0 == memPool)
return false;
PMemoryStore priv = 0,next = 0;
if (0 != memPool->Priv)
priv = memPool->Priv;
if (0 != memPool->Next)
next = memPool->Next;
MemListFree_(memPool->Count);
delete memPool;
memPool = 0;
if (0 != priv)
priv->Next = next;
else
m_Memory = next;
if (0 != next)
next->Priv = priv;
else
m_MemoryEnd = m_Memory;
return true;
}
//解除佔用標記的某塊記憶體 ,釋放類物件前統一釋放
bool MemoryPool_::MemListFree_(int nPoolCount)
{
PMemList list = GetHeadList();
while (list)
{
if (list->nPoolCount == nPoolCount)
{
list->nPoolCount = 0;
list->StartAddress = list->EndAddress = list->CurrentUsgeAmount = 0;
list->bValid = 0;
}
list = list->Next;
}
return true;
}
//釋放指定節點,返回下一個節點
PMemList MemoryPool_::FreeList_(int Count)
{
PMemList list = GetCountList(Count);
if (0 == list)
return 0;
PMemList priv = 0,next = 0;
if (0 != list->Priv)
priv = list->Priv;
if (0 != list->Next)
next = list->Next;
delete list;
if (0 != priv)
priv->Next = next;
else
m_MemHead = next;
if (0 != next)
next->Priv = priv;
else
m_MemEnd = m_MemHead;
return next;
}
//釋放連結串列的所有節點
bool MemoryPool_::FreeList()
{
PMemList list = GetHeadList();
while (list)
{
list = FreeList_(list->Count);
}
return true;
}
下面貼上測試程式碼:
MemoryPool_ pool;
char *Test1 = (char *)pool.MemAlloc(100);
memcpy(Test1,"您好12312321312",sizeof("您好12312321312"));
memset(Test1,0,sizeof(Test1));
char *Test2 = (char *)pool.MemAlloc(100);
memcpy(Test2,"您好12312321312",sizeof("您好12312321312"));
memset(Test2,0,sizeof(Test2));
char *Test3 = (char *)pool.MemAlloc(100 * 1024 * 1024);
memcpy(Test3,"您好12312321312",sizeof("您好12312321312"));
memset(Test3,0,sizeof(Test3));
char *Test4 = (char *)pool.MemAlloc(100 * 1024);
memcpy(Test4,"您好12312321312",sizeof("您好12312321312"));
pool.MemFree(Test1);
Test1 = 0;
pool.MemFree(Test2);
Test2 = 0;
pool.MemFree(Test3);
Test3 = 0;
pool.MemFree(Test4);
Test4 = 0;
Test1 = (char *)pool.MemAlloc(100);
memcpy(Test1,"您好12312321312",sizeof("您好12312321312"));
memset(Test1,0,sizeof(Test1));
Test2 = (char *)pool.MemAlloc(100);
memcpy(Test2,"您好12312321312",sizeof("您好12312321312"));
memset(Test2,0,sizeof(Test2));
Test3 = (char *)pool.MemAlloc(100 * 1024 * 1024);
memcpy(Test3,"您好12312321312",sizeof("您好12312321312"));
memset(Test3,0,sizeof(Test3));
Test4 = (char *)pool.MemAlloc(100 * 1024);
memcpy(Test4,"您好12312321312",sizeof("您好12312321312"));
pool.MemPoolFree();
Test1 = (char *)pool.MemAlloc(100);
memcpy(Test1,"您好12312321312",sizeof("您好12312321312"));
memset(Test1,0,sizeof(Test1));
Test2 = (char *)pool.MemAlloc(100);
memcpy(Test2,"您好12312321312",sizeof("您好12312321312"));
memset(Test2,0,sizeof(Test2));
Test3 = (char *)pool.MemAlloc(100 * 1024 * 1024);
memcpy(Test3,"您好12312321312",sizeof("您好12312321312"));
memset(Test3,0,sizeof(Test3));
Test4 = (char *)pool.MemAlloc(100 * 1024);
memcpy(Test4,"您好12312321312",sizeof("您好12312321312"));