記憶體管理與檢測

工程師WWW發表於2014-02-09

今天開始重構一個DEMO工程,在整理程式碼時發現之前寫過的一個記憶體

管理與記憶體檢測程式碼,再此梳理一番加深下印象。


一.記憶體檢測模組的作用:


在做專案時經常出現記憶體洩漏記憶體訪問越界等情況,當工程比較大時排

查起來會相對比較困難。這個記憶體管理模組的作用就是管理記憶體分配和

使用並在DEBUG環境下統計記憶體使用違規情況



二.記憶體管理模組的基本原理:

  該記憶體管理模組的設計思路是:在使用者請求分配記憶體時記錄請求分配該

記憶體的程式碼環境資訊(如:在哪個檔案中的哪一行請求分配多大記憶體等)

記憶體分配的形式採用記憶體池記憶體塊相結合的方式並在釋放記憶體時檢測

該塊記憶體是否被寫壞(是否有被越界使用)或者該記憶體釋放未被釋放形成了

記憶體洩漏等記錄記憶體使用情況供給使用者分析定位異常情況。

注: 根據一般記憶體使用情況,使用者對小塊記憶體的請求/釋放較為頻繁對連續

的大塊記憶體使用較少或者頻率較低,為了提高記憶體分配的效率可以將頻繁使

用的小塊記憶體使用記憶體池的方式管理,事先分配一塊固定大小的記憶體池每次

使用者請求都從中劃分一塊(具體使用方法下面會詳細記錄),對較大的記憶體塊(如

一次請求分配10M連續大小的記憶體空間)則用記憶體塊方式來管理(具體使用方法下

面會詳細記錄)。


三.記憶體管理模組的基本結構:



1. Memory_Info類;
   
該類用於儲存記憶體分配資訊情況,用於檢測記憶體洩漏及記憶體訪問越界記錄使用者使用記憶體資訊等。
struct Memory_Info
{
    char            sFile[64]; // 儲存呼叫記憶體分配函式的程式碼檔案

    unsigned int    nLine;//儲存呼叫記憶體分配函式的程式碼行
    unsigned int    nUsed; // 使用者請求分配記憶體大小
    unsigned int    nSize; // 記憶體池中標準格式大小
    unsigned int    nCount; // 使用者請求分配個數(陣列)
    testcode   sCode;// 用於檢測記憶體越界的填充碼(可自定義為0x01234567等)
    union
    {
        Memory_Info* next; // 未分配空間時指向下一區域
        char        data[4]; // 分配空間時指向真正使用記憶體區域
    };
};

2. Memory_Pool類;
該類在建立時會記錄自己是多大規格記憶體的緩衝區(如用來儲存4位元組的緩衝區還是用來存
16位元組的緩衝區),這樣做的好處是位元組對齊,事先分配固定大小的記憶體(在DEBUG環境
下分配增加了環境資訊的每個緩衝區單元大小為儲存位元組數(如4位元組)+
sizeof(Memory_Info),RELEASE下可直接分配緩衝區大小位元組)。同時用一個連結串列來管理
這些區域,當使用者請求該大小記憶體時則只需要從連結串列中取出一塊記憶體給使用者使用,釋放時
也並不直接free而是放回當連結串列中。減少了動態開闢記憶體的次數。

class Memory_Pool
{

public:
   Memory_Pool();
   ~Memory_Pool();

  void*   allocate( size_t size, const void* file = 0, unsigned int line = 0 );
  void    free(void* p);

    size_t                  m_size;
    size_t                  m_actual_size;
    Memory_Info*            m_pHeader;
};

注: 在釋放記憶體時會檢測記憶體是否訪問越界,檢測方式也很簡單就是判斷Memory_Info
資料塊的sCode是否發生變化,如果發生了變化則說明有發生過訪問越界,輸出錯誤提
示資訊。Memory_Pool的基本結構是連結串列,通過動態新增/移出連結串列節點實現對使用者的
記憶體釋放和回收記錄記憶體使用情況。

3. Memory_List類;
與Memory_Pool類類似,只是Memory_List得管理方式是雙向連結串列,並且與記憶體池不同
它不會規定分配記憶體的規格格式。

4. MemoryManager類:
管理類對外提供分配記憶體的介面,管理了一個記憶體池陣列(包含多個大小記憶體池如4字
節記憶體池 8位元組記憶體池 16位元組記憶體池... )和一個記憶體列表(用於管理大塊連續記憶體
如大於1M的連續記憶體),根據使用者請求記憶體大小採用不同方式(記憶體池/記憶體列表)分配,
記錄記憶體使用情況等。在程式結束時輸出記憶體使用異常情況。
注: 記憶體分配時的環境資訊如函式,程式碼檔案等可以通過呼叫MemoryManager::
Allocate()介面時傳入,具體方法參見 5.介面對接.

5. 介面對接:
為了是使用者無差別使用new delete等分配記憶體方式,可以採用過載new delete運算子
的方式直接呼叫MemoryManager::Allocate(),或者用巨集定義的方式定義。

void*operator new ( size_t nSize, const char* p, size_t n )
{
     MemoryManager::Allocate( nSize, pFile, nLine );
}


#define  new new( __FILE__, __LINE__ ) 

注: 經過過載new後使用者可以正常使用new來分配記憶體而實際new經替換為我們自定義的
記憶體管理方式來管理。

四.不足:
與正常記憶體相比,用管理器管理會額外增加記憶體空間,且在大量分配大塊連續記憶體資料
時的效能也會稍稍有些影響。

相關文章