• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            春暖花開
            雪化了,花開了,春天來了
            posts - 149,comments - 125,trackbacks - 0
            http://www.vczx.com/article/show.php?id=68

            檢測內存泄漏:
            檢測內存泄漏的關鍵是要能截獲住對分配內存和釋放內存的函數的調用。截獲住這兩個函數,我們就能跟蹤每一塊內存的生命周期,比如,每當成功的分配一塊內存后,就把它的指針加入一個全局的list中;每當釋放一塊內存,再把它的指針從list中刪除。這樣,當程序結束的時候,list中剩余的指針就是指向那些沒有被釋放的內存。這里只是簡單的描述了檢測內存泄漏的基本原理,詳細的算法可以參見Steve Maguire的<<Writing Solid Code>>。
            如果要檢測堆內存的泄漏,那么需要截獲住malloc/realloc/free和new/delete就可以了(其實new/delete最終也是用malloc/free的,所以只要截獲前面一組即可)。對于其他的泄漏,可以采用類似的方法,截獲住相應的分配和釋放函數。比如,要檢測BSTR的泄漏,就需要截獲SysAllocString/SysFreeString;要檢測HMENU的泄漏,就需要截獲CreateMenu/ DestroyMenu。(有的資源的分配函數有多個,釋放函數只有一個,比如,SysAllocStringLen也可以用來分配BSTR,這時就需要截獲多個分配函數)
            在Windows平臺下,檢測內存泄漏的工具常用的一般有三種,MS C-Runtime Library內建的檢測功能;外掛式的檢測工具,諸如,Purify,BoundsChecker等;利用Windows NT自帶的Performance Monitor。這三種工具各有優缺點,MS C-Runtime Library雖然功能上較之外掛式的工具要弱,但是它是免費的;Performance Monitor雖然無法標示出發生問題的代碼,但是它能檢測出隱式的內存泄漏的存在,這是其他兩類工具無能為力的地方。
            以下我們詳細討論這三種檢測工具:
            VC下內存泄漏的檢測方法
            用MFC開發的應用程序,在DEBUG版模式下編譯后,都會自動加入內存泄漏的檢測代碼。在程序結束后,如果發生了內存泄漏,在Debug窗口中會顯示出所有發生泄漏的內存塊的信息,以下兩行顯示了一塊被泄漏的內存塊的信息:
            E:\TestMemLeak\TestDlg.cpp(70)     : {59} normal block at 0x00881710, 200 bytes long.
            Data: <abcdefghijklmnop>     61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70
            第一行顯示該內存塊由TestDlg.cpp文件,第70行代碼分配,地址在0x00881710,大小為200字節,{59}是指調用內存分配函數的Request Order,關于它的詳細信息可以參見MSDN中_CrtSetBreakAlloc()的幫助。第二行顯示該內存塊前16個字節的內容,尖括號內是以ASCII方式顯示,接著的是以16進制方式顯示。
            一般大家都誤以為這些內存泄漏的檢測功能是由MFC提供的,其實不然。MFC只是封裝和利用了MS C-Runtime Library的Debug Function。非MFC程序也可以利用MS C-Runtime Library的Debug Function加入內存泄漏的檢測功能。MS C-Runtime Library在實現malloc/free,strdup等函數時已經內建了內存泄漏的檢測功能。
            注意觀察一下由MFC Application Wizard生成的項目,在每一個cpp文件的頭部都有這樣一段宏定義:
            #ifdef     _DEBUG
            #define     new DEBUG_NEW
            #undef     THIS_FILE
            static     char THIS_FILE[] = __FILE__;
            #endif
            有了這樣的定義,在編譯DEBUG版時,出現在這個cpp文件中的所有new都被替換成DEBUG_NEW了。那么DEBUG_NEW是什么呢?DEBUG_NEW也是一個宏,以下摘自afx.h,1632行
            #define     DEBUG_NEW new(THIS_FILE, __LINE__)
            所以如果有這樣一行代碼:
            char*     p = new char[200];
            經過宏替換就變成了:
            char*     p = new( THIS_FILE, __LINE__)char[200];
            根據C++的標準,對于以上的new的使用方法,編譯器會去找這樣定義的operator new:
            void*     operator new(size_t, LPCSTR, int)
            我們在afxmem.cpp 63行找到了一個這樣的operator new 的實現
            void*     AFX_CDECL operator new(size_t nSize, LPCSTR lpszFileName, int nLine)
            {
                return     ::operator new(nSize, _NORMAL_BLOCK, lpszFileName, nLine);
            }
            void*     __cdecl operator new(size_t nSize, int nType, LPCSTR lpszFileName, int     nLine)
            {
                …
                       pResult = _malloc_dbg(nSize, nType,     lpszFileName, nLine);
                       if (pResult != NULL)
                           return pResult;
                …
            }
            第二個operator new函數比較長,為了簡單期間,我只摘錄了部分。很顯然最后的內存分配還是通過_malloc_dbg函數實現的,這個函數屬于MS C-Runtime Library 的Debug Function。這個函數不但要求傳入內存的大小,另外還有文件名和行號兩個參數。文件名和行號就是用來記錄此次分配是由哪一段代碼造成的。如果這塊內存在程序結束之前沒有被釋放,那么這些信息就會輸出到Debug窗口里。
            這里順便提一下THIS_FILE,__FILE和__LINE__。__FILE__和__LINE__都是編譯器定義的宏。當碰到__FILE__時,編譯器會把__FILE__替換成一個字符串,這個字符串就是當前在編譯的文件的路徑名。當碰到__LINE__時,編譯器會把__LINE__替換成一個數字,這個數字就是當前這行代碼的行號。在DEBUG_NEW的定義中沒有直接使用__FILE__,而是用了THIS_FILE,其目的是為了減小目標文件的大小。假設在某個cpp文件中有100處使用了new,如果直接使用__FILE__,那編譯器會產生100個常量字符串,這100個字符串都是這個cpp文件的路徑名,顯然十分冗余。如果使用THIS_FILE,編譯器只會產生一個常量字符串,那100處new的調用使用的都是指向常量字符串的指針。
            再次觀察一下由MFC Application Wizard生成的項目,我們會發現在cpp文件中只對new做了映射,如果你在程序中直接使用malloc函數分配內存,調用malloc的文件名和行號是不會被記錄下來的。如果這塊內存發生了泄漏,MS C-Runtime Library仍然能檢測到,但是當輸出這塊內存塊的信息,不會包含分配它的的文件名和行號。
            要在非MFC程序中打開內存泄漏的檢測功能非常容易,你只要在程序的入口處加入以下幾行代碼:
            int     tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
            tmpFlag     |= _CRTDBG_LEAK_CHECK_DF;
            _CrtSetDbgFlag(     tmpFlag );
            這樣,在程序結束的時候,也就是winmain,main或dllmain函數返回之后,如果還有內存塊沒有釋放,它們的信息會被打印到Debug窗口里。
            如果你試著創建了一個非MFC應用程序,而且在程序的入口處加入了以上代碼,并且故意在程序中不釋放某些內存塊,你會在Debug窗口里看到以下的信息:
            {47}     normal block at 0x00C91C90, 200 bytes long.
            Data: <            > 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
            內存泄漏的確檢測到了,但是和上面MFC程序的例子相比,缺少了文件名和行號。對于一個比較大的程序,沒有這些信息,解決問題將變得十分困難。
            為了能夠知道泄漏的內存塊是在哪里分配的,你需要實現類似MFC的映射功能,把new,maolloc等函數映射到_malloc_dbg函數上。這里我不再贅述,你可以參考MFC的源代碼。
            由于Debug Function實現在MS C-RuntimeLibrary中,所以它只能檢測到堆內存的泄漏,而且只限于malloc,realloc或strdup等分配的內存,而那些系統資源,比如HANDLE,GDI Object,或是不通過C-Runtime Library分配的內存,比如VARIANT,BSTR的泄漏,它是無法檢測到的,這是這種檢測法的一個重大的局限性。另外,為了能記錄內存塊是在哪里分配的,源代碼必須相應的配合,這在調試一些老的程序非常麻煩,畢竟修改源代碼不是一件省心的事,這是這種檢測法的另一個局限性。
            對于開發一個大型的程序,MS C-Runtime Library提供的檢測功能是遠遠不夠的。接下來我們就看看外掛式的檢測工具。我用的比較多的是BoundsChecker,一則因為它的功能比較全面,更重要的是它的穩定性。這類工具如果不穩定,反而會忙里添亂。到底是出自鼎鼎大名的NuMega,我用下來基本上沒有什么大問題。
            posted on 2008-11-02 10:54 Sandy 閱讀(223) 評論(0)  編輯 收藏 引用 所屬分類: C++
            国内精品综合久久久40p| 久久精品国产亚洲精品2020| 成人综合伊人五月婷久久| 国产99久久精品一区二区| 久久久国产精品| 亚洲精品乱码久久久久久久久久久久 | yy6080久久| 国产精品成人久久久久久久| 97久久国产综合精品女不卡| 99久久er这里只有精品18| 欧美麻豆久久久久久中文| 国产精品久久久天天影视| 少妇人妻综合久久中文字幕| 久久99精品国产99久久6男男| 亚洲伊人久久综合影院| 亚洲一区二区三区日本久久九| 久久久久亚洲av综合波多野结衣| 久久精品成人国产午夜| 久久综合88熟人妻| 久久99九九国产免费看小说| 国内精品久久久久久久久电影网| 久久精品国产亚洲AV无码偷窥 | 久久综合狠狠综合久久综合88| 色播久久人人爽人人爽人人片aV| 97久久精品无码一区二区| 色综合久久久久无码专区| 伊人 久久 精品| 亚洲国产小视频精品久久久三级| 久久国产精品-久久精品| 奇米综合四色77777久久| 久久久久久精品免费免费自慰| 久久久久无码精品| 国产日韩久久免费影院| 日本一区精品久久久久影院| 久久精品aⅴ无码中文字字幕不卡 久久精品aⅴ无码中文字字幕重口 | 伊人久久大香线蕉综合Av | 久久国产综合精品五月天| 色噜噜狠狠先锋影音久久| 国产精品激情综合久久| 国产精品成人久久久久三级午夜电影 | 久久国产V一级毛多内射|