先来大概介绍下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