Lab 2-3: Memory Leakage Detection

 
實習目標 練習運用 crtdbg 函式庫偵測記憶體未釋放的錯誤
 
步驟一 下列程式片段中有記憶體未釋放的錯誤:
    struct File
    {
        char *name;
        char *data;
    } *filePtr;
    ...
    filePtr = (struct File *) malloc(sizeof(struct File));
    filePtr->name = (char *) malloc(20*sizeof(char));
    filePtr->data = (char *) malloc(500*sizeof(char));
    ...
    free(filePtr);
上面這個程式片段中配置給 name 和 data 的 520 個位元組並沒有釋放, 只釋放了配置給 struct File 結構的記憶體。
程式裡有這種錯誤會導致什麼現象? 如果你用過微軟早期的視窗系統的話, 你可能還有印象說系統執行幾天以後就會出現 "系統資源不足", 不穩定當機的現象, 常常就是作業系統裡面的程式或是所執行的應用程式裡有這種記憶體 未釋放的錯誤, 程式只執行幾分鐘或是幾個小時還不會發生什麼問題, 可是一旦長時間執行就穿幫了。
步驟二 下面幾個執行程式都有類似的錯誤, 常常在單獨測試時看不到錯誤, 當撰寫的程式很大時, 要找到究竟在什麼地方發生這種錯誤其實有相當的困難度: 撰寫 C/C++ 程式時由於希望給程式撰寫者最大的操作彈性, 所使用的記憶體的配置與釋放完全由程式設計者來控制, 因此必須徹底了解到 "程式配置多少個位元組的記憶體就必須釋放多少個位元組的記憶體", 在現在個人電腦動輒有一兩 Giga byte 的記憶體, 很容易給人一種錯覺, 覺得少釋放幾十個 byte 應該沒有什麼了不起的, 甚至很難看到系統因此而當掉; 不過這是一種習慣, 一旦養成這種習慣, 你的程式裡就常常會忘記釋放記憶體, 一旦你的程式必須在資源有限的硬體上來運作, 例如: 手機, PDA, 嵌入式系統, 硬體配件 (DVD, 相機, ...) (這些都是目前台灣賺錢的產品), 那不但會讓自己的程式死掉, 也會害整個機器無法運作下去, 一個工作團隊裡如果有一個成員有這種習慣, 整個開發團隊常常必須付出幾倍的偵錯時間。
步驟三 請下載 memory_leak.hmemory_leak.cpp
請撰寫一個使用 malloc 配置記憶體的程式如下:
    #include <malloc.h>
    
    void main()
    {
        int *ptr;
        int i;
        ptr = (int *) malloc(100*sizeof(int));
        for (i=0; i<100; i++)
            ptr[i] = i;
        free(ptr);
    }
請編譯並執行, 如果刪掉 free() 那一列的話, 程式還是可以編譯, 還是可以執行, 但是就有記憶體未釋放的錯誤, 請以下述方法來自動偵測這種錯誤
  1. 將上面程式中 free(ptr); 那一列註解掉
  2. 在此程式最前面加入
        #include "memory_leak.h"
  3. 在 Visual Studio 2008 界面中點選選單 專案/加入現有項目, 將 memory_leak.h 及 memory_leak.cpp 加入此 project
  4. 編輯 memory_leak.h, 確定下列定義的常數值為 0
        #define TEST_MEM_LEAKS_BREAK_NUM  0
  5. 在 main() 函式一開始的地方加入下列函式呼叫
        set_initial_leak_test();
    如下圖打開專案屬性視窗, 點選 C/C++, 命令列, 在其它選項的地方加入 /MTd 以和 LIBCMTD.LIB 偵錯函式庫連結



    編譯,執行就會發現錯誤訊息如下圖
        c:/testMemLeakage3
    
        [Leak test being performed]
        Detected memory leaks!
        Dumping objects ->
        {40} normal block at 0x003707A8, 400 bytes long.
        Data: <                > 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00
        Object dump complete.
        
步驟四 如果希望透過 memory_leak.h 來找到究竟是那一次配置的記憶體沒有釋放, 需要編輯 memory_leak.h 檔案, 將下列中的 0 修改為上面錯誤訊息中的 40
    #define TEST_MEM_LEAKS_BREAK_NUM  40

再編譯, 並且執行, 可以看到下列訊息視窗

點選 除錯, 開啟除錯視窗

如果你的機器上只安裝了 VC2008, 應該會開啟 VC2008 來偵錯, 如果你的機器有安裝其它版本的 VC, 會出現選單讓你挑選, 請挑選 testMemLeak - Microsoft Visual Studio: Visual Studio 2008 那個, 出現下列視窗

按下中斷以後可以看到列畫面:

程式中斷在 dbgheap.c 的 _heap_alloc_dbg() 這個函式裡面, 這不是你寫的, 但是從上面的呼叫堆疊中可以看到, 你的程式 main() 第 11 列呼叫 malloc() 然後呼叫 _nh_malloc_dbg() 然後呼叫 _heap_alloc_dbg();

你可以用滑鼠雙擊 testMemLeak.exe!main() 這一列, 程式編輯視窗就會跳到呼叫 malloc() 那一列, 這個地方配置的記憶體就是沒有釋放掉的記憶體

如果你的偵錯環境中沒有顯示 呼叫堆疊 視窗的話, 你可以在選單 偵錯/視窗選項中找到 (Alt+7), 把 呼叫堆疊 視窗顯示出來

步驟五 請撰寫一個使用 C++ 的 new 配置記憶體的程式如下:
    void main()
    {
        int *ptr;
        int i;

        ptr = new int[100];
        for (i=0; i<100; i++)
            ptr[i] = i;
//        delete ptr;  // 這是一個非常容易犯的錯誤
        delete[] ptr;
    }
請將 delete[] ptr 這一列註解掉, 重複步驟三和步驟四的測試。
步驟六 請向助教說明你如何找到是哪一列的記憶體配置敘述錯誤, 將所完成的 project (去掉 debug/ 資料匣下的所有內容) 壓縮起來, 選擇 Lab2-3 上傳

在你自己的系統上,如果你有灌其他的開發環境 (例如 visual studio 2005), 發生錯誤時系統 可能 不會執行 VC2008 的 debugger, 如果你在選單中找不到, 你可以用下列設定可以改變系統預設的偵錯環境 (你需要有適當的權限)

 
 

C++ 物件導向程式設計課程 首頁

製作日期: 03/01/2012 by 丁培毅 (Pei-yih Ting)
E-mail: pyting@mail.ntou.edu.tw TEL: 02 24622192x6615
海洋大學 電機資訊學院 資訊工程系 Lagoon