前一陣子在項目中使用tcmalloc的heap-checker查找內(nèi)存泄露的問題,今天翻譯一下官方文檔的一篇heap-checker相關(guān)的文章,由于時間有限,沒有完全翻譯完,只翻譯了比較重要的部分,后續(xù)將會補(bǔ)全。
在Google,我們使用heap checker檢查C++程序的內(nèi)存泄露。使用heap checker分三步:將lib庫鏈接到應(yīng)用中;運(yùn)行代碼;分析輸出結(jié)果。
鏈接tcmalloc
heap-checker是tcmalloc的一部分,為了在應(yīng)用程序中安裝heap checker,在程序鏈接階段需要加入-ltcmalloc。但上面這種方法并不是唯一的方式,我們可以使用LD_PRELOAD宏在程序運(yùn)行時加入tcmalloc:
% env LD_PRELOAD="/usr/lib/libtcmalloc.so"
上面這種方式并沒有打開heap checking;僅僅將代碼插入到程序中。因此,在開發(fā)階段我們建議使用鏈接tcmalloc庫的方法,這也是我們在Google使用的方法(然而,我們也可以使用環(huán)境變量開啟profiler,因此通過鏈接tcmalloc庫并不是必須的。)。需要注意的是為了使用heap checker必須使用tcmalloc內(nèi)存分配庫。
運(yùn)行代碼
注意:由于一些安全原因,對于setuid程序,heap profiler在使用的過程中并不會寫文件。
對整個程序進(jìn)行內(nèi)存泄露檢查
我們推薦使用heap-checker的方式是“全程序”模式。在這種模式下,heap-checker在main()函數(shù)開始之前跟蹤內(nèi)存分配,在程序介紹的時候再次檢查。如果發(fā)現(xiàn)有內(nèi)存泄露(任何已經(jīng)分配的內(nèi)存在程序結(jié)束的時候并沒有對象引用它們),程序?qū)袛啵ㄍㄟ^exit(1))并且打印怎樣跟蹤內(nèi)存泄露的信息(使用pprof)
heap-checker會記錄每一次內(nèi)存分配的調(diào)用棧,因此開啟heap-checker會導(dǎo)致程序的內(nèi)存增長并影響程序的性能。
下面介紹怎么開啟全程序模式的內(nèi)存泄露檢查:
-
定義環(huán)境變量 HEAPCHECK 聲明內(nèi)存泄露檢查的模式。例如下面的方式:
% env HEAPCHECK=normal /usr/local/bin/my_binary_compiled_with_tcmalloc
沒有其他的操作了。
需要注意的是heap-checker使用heap-profiling的架構(gòu),因此不可能同時運(yùn)行heap-checker和heap-profiler。
內(nèi)存檢查的特性
如下是在使用全程序內(nèi)存檢查的合法變量:
- minimal
- normal
- strict
- draconian
“minimal”的模式開始內(nèi)存泄露檢查盡可能晚,意味著你可以在初始化例程存在一些內(nèi)存泄露(在main()函數(shù)前),但并不會讓heap-checker記錄。如果你在全局初始化時候存在內(nèi)存泄露,“minimal”模式就很適合。否則,你應(yīng)該使用更加嚴(yán)格的模式。
“normal”模式跟蹤存活對象(live objects)并報告其內(nèi)存泄露信息(在程序結(jié)束時,無法通過存活對象找到的內(nèi)存空間都是內(nèi)存泄露)
“strict”模式與“normal”模式很相似,但“strict”模式會監(jiān)控全局變量析構(gòu)函數(shù)的內(nèi)存泄露。例如,如果你有一個全局變量,在運(yùn)行時申請了部分內(nèi)存,在析構(gòu)函數(shù)中忘記釋放內(nèi)存,在“strict”模式下將會被監(jiān)控到,在“normal”模式下將不會。
“draconian”模式適合想清晰了解應(yīng)用程序內(nèi)存管理的情況,其希望heap-checker去幫助他們優(yōu)化內(nèi)存管理。在“draconian”模式,heap-checker并不會只檢查存活對象,只要有內(nèi)存泄露,其都會報告。
“normal”模式是最常用的模式。
as-is”是一個更加靈活的模式;它允許你自定義heap-checker的一些特性?!發(fā)ocal”激活“顯式heap-check指令”,但并不會開啟任何全程序內(nèi)存泄露檢查。
用于調(diào)試內(nèi)存泄露的小方法
工作原理
當(dāng)一個HeapLeakChecker對象被構(gòu)造的時候,它會在tmp文件夾輸出一個名為<prefix>.<name>-beg.heap的記錄內(nèi)存使用信息的文件。當(dāng)NoLeak()被調(diào)用的時候(對于全程序檢查,其發(fā)生在程序結(jié)束),它將輸出一個名為<prefix>.<name>-end.heap的文件(<prefix>是自動診斷的,<name>是argv[0]的內(nèi)容)。heap-checker將會對比這兩個文件。如果第二個文件使用了過多的內(nèi)存使用量,NoLeaks()函數(shù)將會返回false。對于全程序,這種情況將會引起程序中斷。所有情況下都將會打印如何處理dump文件的信息。