先來大概介紹下Google Heap Profiler,大體有三類功能:node
能夠分析出在程序的堆內有些什麼東西程序員
定位出內存泄露web
可讓咱們知道哪些地方分配了比較多的內存正則表達式
大概的原理就是使用tcmalloc 來代替malloc calloc new等等,這樣Google Heap Profiler就能知道內存的分配狀況,從而分析出內存問題。shell
一. 安裝與簡介 sass
(1) 下載源碼包。路徑https://code.google.com/p/google-perftools/多線程
(2) ./configureapp
(3) make (若是報錯,則./configure --enable-frame-pointers)dom
(4) make installsvg
頭文件和庫文件分別在/usr/local/inlcude/google/和/usr/local/lib/下。須要設置環境變量export LD_LIBRARAY_PATH=/usr/local/lib。跟valgrind的profiler工具的不一樣之處是,Google perftools使用在源程序中插入profiler代碼的方式,而不是valgrind的虛擬機方式,因此Google perftools以庫文件的形式提供了一系列函數接口。爲了使用圖形化結果還須要安裝gv,可以使用apt-get安裝:sudo apt-get install gv。
Google Perftools包括三個工具(注:包括tcmalloc應該是4個),三個工具均支持多線程程序,如下分別介紹。
首先須要把tcmalloc連接到咱們須要分析的程序中, 固然咱們也能夠動態load 這個lib,可是爲了簡單起見,仍是推薦你們連接這個lib到本身的程序中。
連接以後,咱們接下來的任務就是獲得內存分析的dump文件,咱們有兩種方法:
1. 靜態dump方法:
直接定義一個環境變量HEAPPROFILE來指定dumpprofile文件的位置,如:/tmp/test.log,它將會在/tmp/目錄下生成不少相似/tmp/test.log.0003.heap文件名的文件
env HEAPPROFILE="/tmp/test.log" /test/testprog
2. 動態dump方法:
咱們能夠調用Google Heap Profiler的API來控制何時dump出內存的profiler文件,這樣更加靈活,爲此,咱們必須包含heap-profiler.h這個頭文件。
HeapProfilerStart() 用來開始內存分析
HeapProfilerStop(). 用來終止內存分析
這樣就只會在開始和結束之間產生dump profiler文件。
如:
#if 1
#include "acconfig.h"
#ifdefHAVE_GPERFTOOLS_HEAP_PROFILER_H
#include<gperftools/heap-profiler.h>
#else
#include <google/heap-profiler.h>
#endif
#ifdefHAVE_GPERFTOOLS_MALLOC_EXTENSION_H
#include<gperftools/malloc_extension.h>
#else
#include<google/malloc_extension.h>
#endif
#include"common/environment.h"
#endif
void heap_profiler_start()
{
char profile_name[PATH_MAX];
get_profile_name(profile_name, sizeof(profile_name));
HeapProfilerStart(profile_name);
}
void heap_profiler_stop()
{
HeapProfilerStop();
}
void heap_profiler_dump(const char *reason)
{
HeapProfilerDump(reason);
}
bool heap_profiler_running()
{
return IsHeapProfilerRunning();
}
void heap_release_free_memory()
{
MallocExtension::instance()->ReleaseFreeMemory();
}
void heap_profiler_stats(char *buf, int length)
{
MallocExtension::instance()->GetStats(buf, length);
}
程序內存每增加這一數值以後就dump 一次內存,默認是1G (1073741824)
程序若是一次性分配內存超過這個數值dump 默認是100K
查看內存dump文件
這麼dump文件生成以後,咱們接下來就能夠查看內存的分佈狀況,如:
pprof --pdf /test/testProg/tmp/test.log.0001.heap
就是以pdf的形式來顯示這個dump文件,固然咱們也可使用其餘的格式來顯示。
這就是全部可支持的格式。
注:若是pprof 運行出錯,請檢查時候已經正確安裝,若是出現sh: dot: command not found 這個錯誤,就是須要安裝yum install graphviz -y
咱們也能夠專門focus在一些包含某些關鍵字的路徑上,也能夠忽略相關的路徑
--focus
--ignore
pprof --pdf --focus=CData /test/testProg/tmp/test.log.0001.heap
須要安裝:graphviz安裝:
graphviz有多種安裝方式,源碼及發行包。
當前最新版源碼下載:
http://120.221.32.79:6510/www.graphviz.org/pub/graphviz/stable/SOURCES/graphviz-2.38.0.tar.gz
pprof的使用形式都是: pprof --option [ --focus=<regexp> ] [ --ignore=<regexp> ] [--line or addresses or functions] 可執行文件路徑 對應的profile路徑。方括號中的項目是可選項目。<regexp>表示正則表達式
形如:pprof –gif /root/axw/my_biotest/biotestclient.1940324.profile.0055.heap > graph.gif
★options的選項以下:
--text Generate text report
--callgrind Generate callgrindformat to stdout
--gv Generate Postscript and display
--evince Generate PDF and display
--web Generate SVG and display
--list=<regexp> Generate source listing ofmatching routines
--disasm=<regexp> Generate disassembly of matching routines
--symbols Printdemangled symbol names found at given addresses
--dot Generate DOT file to stdout
--ps Generate Postcript to stdout
--pdf Generate PDF to stdout
--svg Generate SVG to stdout
--gif Generate GIF to stdout
--raw Generate symbolized pprof data (useful with remote fetch)
★一些宏
HEAP_PROFILE_ALLOCATION_INTERVAL
程序內存每增加這一數值以後就dump 一次內存,默認是1G (1073741824)
HEAP_PROFILE_INUSE_INTERVAL
程序若是一次性分配內存超過這個數值dump 默認是100K,
命令:pprof --text ./RBtree ./RBtree.prof
關於文本風格輸出結果
序號 |
說明 |
1 |
分析樣本數量(不包含其餘函數調用) |
2 |
分析樣本百分比(不包含其餘函數調用) |
3 |
目前爲止的分析樣本百分比(不包含其餘函數調用) |
4 |
分析樣本數量(包含其餘函數調用) |
5 |
分析樣本百分比(包含其餘函數調用) |
6 |
函數名 |
字符統計結果:
501 62.2% 62.2% 714 88.6% RBTree::insert
84 10.4% 72.6% 84 10.4% RBTree::defaultCmp
80 9.9% 82.5% 154 19.1% RBTree::nodeCmp
61 7.6% 90.1% 73 9.1% RBTree::insertFixup
47 5.8% 95.9% 47 5.8% malloc_trim
9 1.1% 97.0% 746 92.6% main
6 0.7% 97.8% 6 0.7% RBTree::rightRotate
6 0.7% 98.5% 6 0.7% RBTree::leftRotate
5 0.6% 99.1% 5 0.6% malloc
3 0.4% 99.5% 3 0.4% operator new
3 0.4% 99.9% 3 0.4% random_r
1 0.1% 100.0% 1 0.1% rand
0 0.0% 100.0% 755 93.7% __libc_start_main
每行對應一個函數的統計。第一,二列是該函數的本地採樣(不包括被該函數調用的函數中的採樣次數)次數和比例,第三列是該函數本地採樣次數佔當前全部已統計函數的採樣次數之和的比例。第四,五列是該函數的累計採樣次數(包括其調用的函數中的採樣次數)和比例。
Text輸出結果分析:
14 2.1% 17.2% 58 8.7%std::_Rb_tree::find
含義以下:
14:find函數花費了14個profiling samples
2.1%:find函數花費的profiling samples佔總的profilingsamples的比例
17.2%:到find函數爲止,已經運行的函數佔總的profiling samples的比例
58:find函數加上find函數裏的被調用者總共花費的profilingsamples
8.7%:find函數加上find函數裏的被調用者總共花費的profilingsamples佔總的profiling samples的比例
std::_Rb_tree::find:表示profile的函數
ps: 100samples a second,因此得出的結果除以100,得秒單位
命令:
Pprof --pdf /root/biotest /var/log/client.1940324.profile.0047> graph.gif 生成的gif統計圖以下。
結果如圖:
圖中每一個節點對應一個函數,節點中的文字分別爲類名,函數明,本地採樣次數比例和累計採樣次數比例(若是跟本地相同則省略)。每條邊表示一個函數調用關係:caller調用callee,邊上的數字表示callee中由於caller調用而被採樣的次數。
pprof若是不帶任何選項調用(只有可執行文件路徑和profile文件路徑)則進入互動模式,在互動模式下可以使用gv,gif,text等命令來替代前面介紹的帶選項的pprof調用。
命令:pprof --pdf --base /tmp/test.log.0001.heap /test/testProg/tmp/test.log.0101.heap
爲了知道在某一段時間內的內存分佈狀況,或者須要瞭解某段時間內有沒有內存泄露,咱們就須要用到diff咱們的dump文件
例如:pprof --pdf --base /tmp/test.log.0001.heap /test/testProg/tmp/test.log.0101.heap
比較了第一個dump文件與第101個文件的差別,並且結果以pdf的形式顯示
堆內存泄漏檢測工具。使用簡單,先在連接被檢查程序的時候用-ltcmalloc選項鍊接Goolge Perftools的堆內存管理庫tcmalloc(tcmalloc會替代C的堆內存管理庫),而後每次用命令行「env HEAPCHECK=normal 可執行程序路徑」來進行檢查,其中檢查形式normal能夠替換成其餘值,檢查的結果會以屏幕報告的形式給出。如下給出一個實例:
# cat test_heap_checker.cpp
#include <cstdio>
#include <cstdlib>
int* fun(int n)
{
int *p1=new int[n];
int *p2=new int[n];
return p2;
}
int main()
{
int n;
scanf("%d",&n);
int *p=fun(n);
delete [] p;
return 0;
}
# g++ -O0 -g test_heap_checker.cpp -ltcmalloc -o test_heap_checker
# env HEAPCHECK=normal /home/hongcheng/mycode/google-perftools-tests/test_heap_checker
WARNING: Perftools heap leak checker is active -- Performance may suffer
100
Have memory regions w/o callers: might report false leaks
Leak check _main_ detected leaks of 400 bytes in 1 objects
The 1 largest leaks:
Leak of 400 bytes in 1 objects allocated from:
If the preceding stack traces are not enough to find the leaks, try running THIS shell command:
pprof /home/hongcheng/mycode/google-perftools-tests/test_heap_checker "/tmp/test_heap_checker.13379._main_-end.heap" --inuse_objects --lines --heapcheck --edgefraction=1e-10 --nodefraction=1e-10 --gv
If you are still puzzled about why the leaks are there, try rerunning this program with HEAP_CHECK_TEST_POINTER_ALIGNMENT=1 and/or with HEAP_CHECK_MAX_POINTER_OFFSET=-1
Exiting with error code (instead of crashing) because of whole-program memory leaks
上面的報告顯示有400個字節的內存泄漏,並提示使用pprof進一步跟蹤泄漏來源的方法。
包括normal在內總共有4種泄漏檢查方式:minimal,忽略進入main函數以前的初始化過程;normal,報告全部的沒法再引用的內存對象;strick,在normal的基礎上增長一些額外的檢查;draconian,在程序退出的時候存在未釋放的內存的狀況下報錯。
除了前面使用env命令行的全局內存泄漏檢查方式外,還能夠做對代碼段的更加細粒度的泄漏檢查。這裏須要先在源代碼中包含頭文件google/heap-checker.h。下面是一個檢查代碼段的實例:
HeapLeakChecker heap_checker("test_foo");
{
code that exercises some foo functionality;
this code should preserve memory allocation state;
}
if (!heap_checker.SameHeap()) assert(NULL == "heap memory leak");
在進入代碼段以前創建當前堆內存使用狀況的snapshot,而後在結束代碼段的時候經過與記錄的snapshot對比檢查是否有泄漏。方法NoLeaks()也能夠用在這裏。下面是一個實例:
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <google/heap-checker.h>
int* fun(int n)
{
int *p2;
HeapLeakChecker heap_checker("fun");
{
new int[n];
p2=new int[n];
//delete [] p1;
}
assert(!heap_checker.NoLeaks());
return p2;
}
int main(int argc,char* argv[])
{
int n;
scanf("%d",&n);
int *p=fun(n);
delete [] p;
return 0;
}
注意被檢查程序的main函數形式必須爲帶2個參數的形式,不然會在編譯時報告重複定義。運行env命令行將會報告assert失敗。
另外,還能夠跳過某些代碼段的檢查,方式以下:
{
HeapLeakChecker::Disabler disabler;
<leaky code>
}
<leaky code>處的代碼將被heap-checker忽略。
[root@inspur178 my_biotest]# HEAPCHECK=normal ./biotest -w 1/biotest1/ -s 34 -b 4096
WARNING: Perftools heap leakchecker is active -- Performance may suffer
------processID -> 2391436
thread index : 1 -> /1.dat
begin write...
------processID -> 2391436
input command: end write .
thread [1] complete !
c:(null) p:(null)
input command: quit
c:quit p:(null)
wait join all the thread...
all thread over!!!
Have memory regions w/o callers:might report false leaks
Leak check _main_ detected leaksof 144 bytes in 1 objects
The 1 largest leaks:
Using local file ./biotest.
Leak of 144 bytes in 1 objectsallocated from:
@7fa290af3d88 icfs_os_setxattr
@7fa290aef8c9 IcfsContext
@7fa290aebec5 common_preinit
@7fa2909207bb icfs_create
@4037bc main
@7fa28fa23d1d __libc_start_main
@4016e9 _start
If the preceding stack traces arenot enough to find the leaks, try running THIS shell command:
pprof ./biotest"/tmp/biotest.2391436._main_-end.heap" --inuse_objects --lines--heapcheck --edgefraction=1e-10--nodefraction=1e-10 --gv
If you are still puzzled aboutwhy the leaks are there, try rerunning this program withHEAP_CHECK_TEST_POINTER_ALIGNMENT=1 and/or withHEAP_CHECK_MAX_POINTER_OFFSET=-1
If the leak report occurs in asmall fraction of runs, try running with TCMALLOC_MAX_FREE_QUEUE_SIZE of fewhundred MB or with TCMALLOC_RECLAIM_MEMORY=false, it might help find leaks morere
Exiting with error code (insteadof crashing) because of whole-program memory leaks