cpp 如何 debug memory leak?

哭了。

python就從來不leak

java也不leak

cpp就leak。。而且還給我一大串16進位內存地址讓我自己去看?我好醉

有什麼辦法可以告訴我是發生於哪一個cpp symbol么?


Windows平台:

先確保有一個所有cpp都會include的頭文件,比如stdafx.h,在裡面加入

#include &

#ifdef _DEBUG

#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)

#endif // _DEBUG

然後在main函數第一句寫

_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);

接下來所有你自己代碼造成的內存泄露都會在調試結束後顯示出來並且有cpp和行號,雙擊就會跳轉過去

Linux:

同Xi Yang的回答


valgrind,編譯器自帶的內存檢查器。

最重要的,良好的工程設計。


恰恰相反,工作近10年來,我只見過 java 程序發生泄漏,一次也沒見過 C++ 程序泄漏。至於 python,都是寫一些 script,運行幾分鐘就退出了,也不用關心是否泄漏。

在 C++ 里,任何資源釋放的操作都應該在析構函數里進行,這樣只要管好對象生命期就不會有資源泄漏了。


使用 valgrind 或者 asan 可以列印出發生泄漏的內存是哪行代碼分配的。

比如asan:

對於動態內存泄露,就是業務邏輯不斷在堆上面分配內存但在程序退出的時候會全部釋放這種,可以使用tcmalloc的heap profile:

檢測出分配內存比較多的函數然後自行分析。

偶爾會有以上幾個工具無法使用的情況,這時對於純C程序可以使用mcheck.h工具:

不跑toy例子了。

C++程序(使用new關鍵字)可以自己hook到malloc裡面然後利用backtrace列印調用棧來獲得內存分配信息:

然而實際寫起來是很麻煩滴~

以上,希望有用。


valgrind對於高並發場景的泄露跟蹤效果很有限,而且有些場景下,由於有內存池的管理,從進程全局來看並沒有泄漏,而只是沒有還給內存池,浪費的內存仍然可以被內存池中的指針索引到。所以最有效的辦法也最簡單暴力,第一,封裝malloc/free,增加module參數來標記使用者,定期列印所有module的使用量統計,幫你確定是哪個模塊哪個類泄漏了,很多時候有這個提示,再看看代碼就分析出來了;第二,實在沒辦法了,就加日誌,申請時把指針和堆棧打出來,釋放時把指針打出來,寫一段簡單的awk就能分析日誌了


控制好代碼質量才是關鍵,別等出問題了再問怎麼查,你把new和delete都封到構造、析構函數裡面就不會有這些問題。


別用裸指針,用sharedptr和uniqueptr,搞清楚資源所有權

MSVC的話,裝個VLD,CRT自己也有debug內存泄漏的函數,自己查MSDN


一言不和就黑C++

先別急著查bug了,先做好單元測試和重構吧。能出內存泄露,代碼肯定有不小的問題,單元測試做好了,集成時不太可能內存泄露。


學好RAII,用好以下幾個ptr,shared_ptr/scoped_ptr/unique_ptr之後,基本見不到內存泄露了。

如果還有內存泄露,鏈上tcmalloc看內存佔用圖,或上valgrind都能很方便查出來內存佔用。

倒是python,有的變數明明我已經所有引用都沒了,為啥不給我釋放...手工調用gc都不生效...導致佔了我們大量內存,現在還沒查出來原因...


我不是針對誰,我是說絕大多數 c++ 程序員(陳碩、輪子等編程能手除外),基本上他們的代碼里只要出現了裸指針,內存泄漏是早晚的事兒。尤其是客戶端項目,基本上我半路進去的項目,用點心都能查出一堆內存泄漏和潛在的內存泄漏。

所以既然用了 c++,就別想著事後補救,寫代碼的時候就別高估自己,老老實實的用正常方式寫代碼:

  • 放棄 malloc。

  • 把 new 放進構造函數,delete 放進析構函數。注意是全部,不是局部!

  • 上面一條做不到就用智能指針。

  • 實在需要裸指針,也不要把裸指針從一個線程往另一個線程傳遞,最好一個類往另一個類傳遞都不要。

  • 補充一條,有一些框架採用一種對象樹機制,每個對象都有個 parent,parent 析構子對象全部析構。如果自己寫代碼,我不建議設計成這樣,代價太大,而且不能靈活應對各種情況。

已經出問題的項目,有各種工具幫你查找問題,大家都說了我就不重複了。另外我有個土辦法——

重載 new 或者改構造函數,打日誌,然後啟動程序跑一個完整流程,然後寫腳本分析日誌。具體地說,你每次分配內存的時候都能得到一個地址,delete 的時候自然也能記錄到被 delete 的地址,如果 log 裡面出現了 new/delete 不成對的地址,那顯然是有問題的。怎麼通過這個地址找到代碼,辦法很多,基本上只要能確定泄露對象的 size,就能猜個八九不離十了。


python、java不會內存泄漏?!

雖然java把釋放資源的操作交給gc了,但只要有活的引用在,照樣不是釋放不了。。。

檢查內存泄露倒是的確java方便(雖然我依舊解決不能)。

C++的話只能監控每一次new delete了


查漏很多朋友都說過了。我補充點防漏的原則。

cpp需要你自己管理堆內存。這不複雜,但需要編碼時有意的規範分配行為。

應通過統一介面進行堆分配/釋放。這樣必要時可以追蹤分配點。

總是以對象為單位管理。不是對象的封裝成對象。讓內存分配與對象生存期掛鉤。

明確對象間隸屬關係。按所有權逐層管理。不越權持有非共享對象。

對於共享對象,通過引用計數控制。

對於跨模塊/跨控制流共享的對象,酌情以創建副本或通過間接訪問來代替直接引用。

總之儘可能縮小對象的活動範圍。儘可能分層降解管理的複雜度。


不想自己管理內存就別用C++


一、對於VS2005/VS2008編譯器自帶的內存檢測工具/函數。

在 main() 函數開頭加上:

#include 「crtdbg.h」

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);

二、BoundsChecker之類的檢測工具。

BoundsChecker 是一個Run-Time錯誤檢測工具,它主要定位程序在運行時期發生的各種錯誤。BoundsChecker能檢測的錯誤包括:

1、指針操作和內存、資源泄露錯誤,比如:內存泄露;資源泄露;對指針變數的錯誤操作。

2、內存操作方面的錯誤,比如:內存讀、寫溢出;使用未初始化的內存。

3、API函數使用錯誤

三、linux下可以用valgrind檢測內存泄露錯誤。

四、purify工具,這個是專門檢測內存的,包括泄露、越界、指針跑飛等都可以檢查,在VC上使用方便。

五、用Windbg,試過查句柄泄漏的,比較方便。

六、Visual Leak Detector,一款用於Visual C++的免費的內存泄露檢測工具。

源自 C++內存泄露及處理方法


1.少用裸指針,多動腦子想替代方案。我不是不讓你用,君不見上下五千年積累下來的c和c++代碼裡面多少裸指針。

2.visual leak detector,這個工具基本相當於玩具。

3.intel inspector XE。強大的內存錯誤檢測工具,能檢測內存增長,非法的內存訪問等等。但是收費。

4.visual studio2013以上版本在win10上自帶內存增長監測工具。

5.別把鍋甩給c++。所有的內存都是你分配的,就算是java,就算自帶gc,你玩到內存爆炸也不是沒可能,關鍵是你怎麼用了。

PS:我是windows程序猿,你們說的那些valgrind什麼的我不懂。


老實說啊,我不用gperf valgrind這些工具,就是純print也能找到leaks,就是慢點。所以這個東西更多的是思路,是你對整個代碼結構的理解,以及是否可以合理的拆分模塊單元測試。


Visual Leak Detector for Visual C++ 2008-2015

用這個,可以檢測到內存泄漏,並列印相應的行和堆棧出來,可以應付大部分情況了


如果你真想知道哪裡泄露了,還真有一些辦法:C++內存泄露檢查方法


簡單說一下幾種方法。

1、valgrind,非常常見和通用的內存泄漏檢測工具。優點:簡單易操作,分析報告完整,提供源碼,支持大部分平台。缺點:運行太慢了。

2、asan:非常好用的工具,貌似現在gcc自帶了,使用方法見 @鄧沐陽 的說明。強烈推薦

3、使用第三方的內存lib替換glibc,這些工具都提供了自己的內存泄漏檢測工具

4、自己實現new和delete,難度係數很高,性價比低

5、intel最近出了一個非常強大的工具,好像還得在它最新的核心上運行才行。

手機碼字,技術細節有限,只能提供幾個思路給題主參考。

最後,也是最簡單直接,最終極的方法:代碼隔離+RTFSC

在此簡單為cpp正名:隨著現在編程理念的普及和代碼review以及各種代碼質量工具的保障,內存泄漏問題,遇到的不多。比較苦逼的還是在多線程下的死鎖和Data race

陳碩說的很好,除非必要,少用裸指針。


linux

g++ -fsanitize=address -O0 -g

具體自己谷歌吧

C++11已經有各種智能指針,當然光靠這個也不夠


推薦閱讀:

C++中類的構造函數,成員變數是在初始化列表初始化還是在函數體中進行賦值?
cout << sizeof(vector<int>);輸出是32,為什麼?
如何用 VS 2013 打包 VC++ 程序?
設計C++函數傳參時如何決定使用指針還是引用?
C++的編譯單元要知道所有的實現?

TAG:編程語言 | 編程 | C |