最近在项目中用到了google的heap profiler工具来分析内存占用,效果非常显著,因此在这里写一篇博客记录一下使用过程中遇到的一些问题。

heap profiler依赖于tcmalloc,所以先要在本机安装tcmalloc,安装过程非常的简单。然后开始使用tcmalloc进行编译自己写的程序。

  • 生成堆栈快照

先写一段申请大量内存的代码:

heap_profiler.cpp

   #include <iostream>
#include <unistd.h> int* create(unsigned int size)
{
return new int[size];
} int main()
{
int count = ;
int* array[count]; unsigned int size = * ;
for (int i = ; i < count; ++i) {
sleep();
array[i] = create( * size); int* b = new int[ * size];
} for (int i = ; i < count; ++i) {
delete[] array[i];
}
}

接着进行编译:

$ g++ heap_profiler.cpp -ltcmalloc -g -o main

然后执行如下命令:

$ HEAPPROFILE=test ./main
Starting tracking the heap
Dumping heap profile to test..heap ( MB currently in use)
Dumping heap profile to test..heap ( MB currently in use)
Dumping heap profile to test..heap ( MB currently in use)
Dumping heap profile to test..heap ( MB currently in use)
Dumping heap profile to test..heap (Exiting, MB in use)
$ ls
heap_profiler.cpp main test..heap test..heap test..heap test..heap test..heap

可以看到,这里生成了几个.heap文件,并且知道程序退出时,还有80M的内存在未释放。使用pprof命令即可对这些文件进行分析。

这里需要特别注意一点,笔者之前因为项目本身用的tcmalloc是采用静态链接方式,即如下所示,编译时,静态链接了static_lib下的libtcmalloc.a:

$ ls -lh
total .0K
-rw-rw-r-- minglee minglee Dec : heap_profiler.cpp
drwxrwxr-x minglee minglee .0K Dec : static_lib $ ls -lh static_lib/
total 5.7M
-rw-rw-r-- minglee minglee 5.6M Dec : libtcmalloc.a $ g++ heap_profiler.cpp -ltcmalloc -g -o main -Lstatic_lib/ -lpthread
static_lib//libtcmalloc.a(stacktrace.o): In function `GetStackTraceWithContext_libunwind(void**, int, int, void const*)':
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_Ux86_64_getcontext'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_init_local'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_step'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_step'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_get_reg'
static_lib//libtcmalloc.a(stacktrace.o): In function `GetStackTrace_libunwind(void**, int, int)':
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_Ux86_64_getcontext'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_init_local'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_step'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_step'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_get_reg'
static_lib//libtcmalloc.a(stacktrace.o): In function `GetStackFramesWithContext_libunwind(void**, int*, int, int, void const*)':
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_Ux86_64_getcontext'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_init_local'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_step'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_get_reg'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_step'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_get_reg'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_get_reg'
static_lib//libtcmalloc.a(stacktrace.o): In function `GetStackFrames_libunwind(void**, int*, int, int)':
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_Ux86_64_getcontext'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_init_local'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_step'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_get_reg'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_step'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_get_reg'
/home/minglee/install_packet/gperftools-2.7/src/stacktrace_libunwind-inl.h:: undefined reference to `_ULx86_64_get_reg'
collect2: error: ld returned exit status $ g++ heap_profiler.cpp -ltcmalloc -g -o main -Lstatic_lib/ -lpthread -lunwind $ HEAPPROFILE=test ./main
$

导致执行的时候没有任何的反应,也不会出现 “Starting tracking the heap” 提示,更不会生成 .heap 文件。所以切记使用heap_profiler的时候需要使用动态链接,如果不想使用动态链接,也可以通过加代码的方式去生成.heap文件:

   #include <iostream>
#include <unistd.h>
#include <gperftools/heap-profiler.h> int* create(unsigned int size)
{
return new int[size];
} int main()
{
HeapProfilerStart("test");
int count = ;
int* array[count]; unsigned int size = * * ;
for (int i = ; i < count; ++i) {
sleep();
array[i] = create(size); int* b = new int[2 * size];
} for (int i = ; i < count; ++i) {
delete[] array[i];
}
HeapProfilerStop();
}

注意 第12行 和 第27行 增加的两个函数,HeapProfilerStart() 和 HeapProfilerStop()(头文件在<gperftools/heap-profiler.h>中),分别用来开启和关闭堆栈分析器,HeapProfilerStart() 需要一个参数,这个参数就是.heap文件(也就是堆栈快照)的前缀。这个前缀也可以通过环境变量 HEAPPROFILE 来设置。这也编译出来的代码,直接执行,也可以生产.heap文件:

$ g++ heap_profiler.cpp -ltcmalloc -g -o main -Lstatic_lib/ -lpthread -lunwind
$ ./main
Starting tracking the heap
Dumping heap profile to test..heap ( MB currently in use)
Dumping heap profile to test..heap ( MB currently in use)
Dumping heap profile to test..heap ( MB currently in use)
Dumping heap profile to test..heap ( MB currently in use)

可以看到,使用动态编译的方式libtcmalloc的方式来使用heap profiler能显示出更多的信息,比如程序退出时是否有未释放的内存。以下的分析阶段都是采用动态编译的方式进行。

  • 使用pprof命令进行分析:
$ HEAPPROFILE=test ./main
Starting tracking the heap
Dumping heap profile to test..heap ( MB currently in use)
Dumping heap profile to test..heap ( MB currently in use)
Dumping heap profile to test..heap ( MB currently in use)
Dumping heap profile to test..heap ( MB currently in use)
Dumping heap profile to test..heap (Exiting, MB in use)
$ pprof --text main test..heap
Using local file main.
Using local file test..heap.
Total: 480.0 MB
400.0 83.3% 83.3% 400.0 83.3% create
80.0 16.7% 100.0% 480.0 100.0% main
0.0 0.0% 100.0% 480.0 100.0% __libc_start_main

可以很清晰的看到内存分配的函数以及分配的内存总量。各列含义的解读:

  • 第一列包含直接占用的内存
  • 第四列包含自身和所有被调用的函数占用的内存
  • 第二列和第五列仅仅是第一列和第四列数字的百分比表示
  • 第三列是第二列从第一行到当前行的累加值。(比如:二行三列= 一行二列 + 二行二列; 三行三列 = 一行二列 + 二行二列 + 三行二列)

另外还可以加上--stack选项(与--text同时使用):

$ pprof --text --stack main test..heap
Using local file main.
Using local file test..heap.
Total: 480.0 MB
Stacks: (00000000004009aa) /home/minglee/workspace/test_code/heap_profiler/heap_profiler.cpp::main
(00007f9644cb3c04) ??::__libc_start_main (00000000004008b8) /home/minglee/workspace/test_code/heap_profiler/heap_profiler.cpp::create
(000000000040096d) /home/minglee/workspace/test_code/heap_profiler/heap_profiler.cpp::main
(00007f9644cb3c04) ??::__libc_start_main Leak of bytes in objects allocated from:
@ 004008b8 unknown
@ 000000000040096d main /home/minglee/workspace/test_code/heap_profiler/heap_profiler.cpp:
@ 00007f9644cb3c04 __libc_start_main ??:
Leak of bytes in objects allocated from:
@ 004009aa unknown
@ 00007f9644cb3c04 __libc_start_main ??: 400.0 83.3% 83.3% 400.0 83.3% create
80.0 16.7% 100.0% 480.0 100.0% main
0.0 0.0% 100.0% 480.0 100.0% __libc_start_main

--text的选项,在查看简单的程序时还是不错的,但是面对复杂的程序时,就显得心有余力不足了。这个时候可以使用--gv选项:

$ pprof --gv main test..heap
Using local file main.
Using local file test..heap.
Dropping nodes with <= 2.4 MB; edges with <= 0.5 abs(MB)
sh: dot: command not found

这里报错是因为--gv选项需要安装 graphviz 和 gv:

$ sudo yum install graphviz gv

安装完之后如果报出如下错误:

$ pprof --gv main test..heap
Using local file main.
Using local file test..heap.
Dropping nodes with <= 2.4 MB; edges with <= 0.5 abs(MB)
gv: Unable to open the display.

说明无法打开显示器,也就是说,--gv选项,需要在带图形界面的系统上使用。转到图形界面系统上做分析,可以得到下图:

可以看到main函数占用了80M内存,占所有占用内存的16.7%,main直接或间接占用了内存480M,占所有未释放内存的100%,下面的create函数占用内存400M,占所有未释放内存的83.3%。显示的结果非常清晰明了,能够清晰的定位到问题。

此外,为了生成明确的堆栈,编译优化建议不要开,O0就好,最好再加上编译选项 -fno-omit-frame-pointer 这样能更好的显示出完整堆栈,定位起问题来会更加的轻松。

使用heap profiler进行内存占用分析的更多相关文章

  1. Unity3D–Texture图片空间和内存占用分析(转载)

    原地址:http://www.unity蛮牛.com/home.php?mod=space&uid=1801&do=blog&id=756 Texture图片空间和内存占用分析 ...

  2. Unity3D–Texture图片空间和内存占用分析

    Texture图片空间和内存占用分析.由于U3D并没有很好的诠释对于图片的处理方式,所以很多人一直对于图集的大小和内存的占用情况都不了解.在此对于U3D的图片问题做一个实际数据的分析.此前的项目都会存 ...

  3. PHP查询MySQL大量数据的内存占用分析

    这篇文章主要是从原理, 手册和源码分析在PHP中查询MySQL返回大量结果时, 内存占用的问题, 同时对使用MySQL C API也有涉及. 昨天, 有同事在PHP讨论群里提到, 他做的一个项目由于M ...

  4. 性能分析 | Linux 内存占用分析

    这篇博客主要介绍 linux 环境下,查看内存占用的两种方式:使用 ps,top等命令:查看/proc/[pid]/下的文件.文章简要介绍了命令的使用方法与一些参数意义,同时对/proc/[pid]/ ...

  5. WPF 属性系统 依赖属性之内存占用分析

    关于WPF的属性系统园子内有不少这方面的文章.里面大都提到了WPF依赖属性的在内存方面的优化.但是里面大都一笔带过.那么WPF到底是怎么样节约内存的.我们通过WPF属性和普通的CLR属性对比来看一下W ...

  6. linux内存占用分析

    概述 想必在linux上写过程序的同学都有分析进程占用多少内存的经历,或者被问到这样的问题——你的程序在运行时占用了多少内存(物理内存)?通常我们可以通过top命令查看进程占用了多少内存.这里我们可以 ...

  7. Spark BlockManager的通信及内存占用分析(源码阅读九)

    之前阅读也有总结过Block的RPC服务是通过NettyBlockRpcServer提供打开,即下载Block文件的功能.然后在启动jbo的时候由Driver上的BlockManagerMaster对 ...

  8. 项目中Map端内存占用的分析

      最近在项目中开展重构活动,对Map端内存尽量要省一些,当前的系统中Map端内存最高占用大概3G左右(设置成2G时会导致Java Heap OOM).虽然个人觉得占用不算多,但是显然这样的结果想要试 ...

  9. 【经验】使用Profiler工具分析内存占用情况

    Unity3D为我们提供了一个强大的性能分析工具Profiler.今天我们就使用Profiler来具体分析一下官方样例AngryBots的内存使用信息数据. 首先打开Profiler选择Memory选 ...

随机推荐

  1. Communications link failure,The last packet successfully received from the server was *** millisecon

    使用Connector/J连接MySQL数据库,程序运行较长时间后就会报以下错误: Communications link failure,The last packet successfully r ...

  2. Oracle中Inner join和Where的区别

    1 .Where子句中使用的连接语句,在数据库语言中,被称为隐性连接.Inner join--on子句产生的连接称为显性连接.(其他Join参数也是显性连接)Where 和Inner join产生的连 ...

  3. 关于gradle加快构建速度采用阿里云中央仓库的配置

    近期开始了一段新的开始,在一家在线教育的公司开始下一阶段的工作,鉴于之前的面试中问到了spring的内容基本快要到源码层面的问题了,想要把spring的源码导到idea中,结果出现了下载极慢的问题,如 ...

  4. 关于json格式字符串解析并用mybatis存入数据库

    园子里面找了很多关于json解析后存入数据库的方法,不是太乱,就是没有写完,我下面的主题代码多是受下面两位的启发,请按顺序查看 http://www.cnblogs.com/tian830937/p/ ...

  5. Linux环境下安装Nexus

    JDK的安装本文不在说了. 直接进入正题. 1. wget http://www.sonatype.org/downloads/nexus-2.13.0-01-bundle.tar.gz 获取nexu ...

  6. springboot注册bean失败

    启动的主类应该放在和其他包一样的目录,不能放在一个目录里面

  7. Linux VPS上DenyHosts阻止SSH暴力攻击

    2009年07月23日 下午 | 作者:VPS侦探 现在的互联网非常不安全,很多人没事就拿一些扫描机扫描ssh端口,然后试图连接ssh端口进行暴力破解(穷举扫描),所以建议vps主机的空间,尽量设置复 ...

  8. Linux 制作补丁 打补丁 撤销补丁

    1.制作补丁 diff - 逐行比较文件 格式 diff   参数   旧文件/旧文件夹   新文件/新文件夹 -N   将不存在的文件看作是空的 -a   将所有文件都视为文本文件 -u   以合并 ...

  9. Mybatis数据的增删改查

    数据: Student{id int,name String ,age int} 配置mybatis-config.xml <?xml version="1.0" encod ...

  10. ActiveMQ入门之四--ActiveMQ持久化方式

    消息持久性对于可靠消息传递来说应该是一种比较好的方法,有了消息持久化,即使发送者和接受者不是同时在线或者消息中心在发送者发送消息后宕机了,在消息中心重新启动后仍然可以将消息发送出去,如果把这种持久化和 ...