使用heap profiler进行内存占用分析
最近在项目中用到了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进行内存占用分析的更多相关文章
- Unity3D–Texture图片空间和内存占用分析(转载)
原地址:http://www.unity蛮牛.com/home.php?mod=space&uid=1801&do=blog&id=756 Texture图片空间和内存占用分析 ...
- Unity3D–Texture图片空间和内存占用分析
Texture图片空间和内存占用分析.由于U3D并没有很好的诠释对于图片的处理方式,所以很多人一直对于图集的大小和内存的占用情况都不了解.在此对于U3D的图片问题做一个实际数据的分析.此前的项目都会存 ...
- PHP查询MySQL大量数据的内存占用分析
这篇文章主要是从原理, 手册和源码分析在PHP中查询MySQL返回大量结果时, 内存占用的问题, 同时对使用MySQL C API也有涉及. 昨天, 有同事在PHP讨论群里提到, 他做的一个项目由于M ...
- 性能分析 | Linux 内存占用分析
这篇博客主要介绍 linux 环境下,查看内存占用的两种方式:使用 ps,top等命令:查看/proc/[pid]/下的文件.文章简要介绍了命令的使用方法与一些参数意义,同时对/proc/[pid]/ ...
- WPF 属性系统 依赖属性之内存占用分析
关于WPF的属性系统园子内有不少这方面的文章.里面大都提到了WPF依赖属性的在内存方面的优化.但是里面大都一笔带过.那么WPF到底是怎么样节约内存的.我们通过WPF属性和普通的CLR属性对比来看一下W ...
- linux内存占用分析
概述 想必在linux上写过程序的同学都有分析进程占用多少内存的经历,或者被问到这样的问题——你的程序在运行时占用了多少内存(物理内存)?通常我们可以通过top命令查看进程占用了多少内存.这里我们可以 ...
- Spark BlockManager的通信及内存占用分析(源码阅读九)
之前阅读也有总结过Block的RPC服务是通过NettyBlockRpcServer提供打开,即下载Block文件的功能.然后在启动jbo的时候由Driver上的BlockManagerMaster对 ...
- 项目中Map端内存占用的分析
最近在项目中开展重构活动,对Map端内存尽量要省一些,当前的系统中Map端内存最高占用大概3G左右(设置成2G时会导致Java Heap OOM).虽然个人觉得占用不算多,但是显然这样的结果想要试 ...
- 【经验】使用Profiler工具分析内存占用情况
Unity3D为我们提供了一个强大的性能分析工具Profiler.今天我们就使用Profiler来具体分析一下官方样例AngryBots的内存使用信息数据. 首先打开Profiler选择Memory选 ...
随机推荐
- 高度注意 Map 类集合 K/V 能不能存储 null 值的情况
集合类 Key Value Super 说明 Hashtable 不允许为 null 不允许为 null Dictionary 线程安全 ConcurrentHashMap 不允许为 null 不允许 ...
- Django的MultipleChoiceField处理小技巧
1.如果遇到多选择的,在收到数据时一般需要做处理 tag_id = fields.MultipleChoiceField( choices=[], widget=widgets.CheckboxSel ...
- 如何理解HTTP协议的 “无连接,无状态” 特点?
HTTP 是一个属于应用层的面向对象的协议,HTTP 协议一共有五大特点:1.支持客户/服务器模式:2.简单快速:3.灵活:4.无连接:5.无状态. 无连接 无连接的含义是限制每次连接只处理一个请求. ...
- CAS原理分析
在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁(后面的章节还会谈到锁). 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度 ...
- 【转】VC 线程间通信的三种方式
原文网址:http://my.oschina.net/laopiao/blog/94728 1.使用全局变量(窗体不适用) 实现线程间通信的方法有很多,常用的主要是通过全局变量.自定义消息和 ...
- 02 - Unit01:服务器返回数据的json处理+搭建项目环境
服务器返回数据的json处理+搭建项目环境 服务器返回数据的json处理 springMVC JSP响应流程 请求 -->DispatcherServlet -->HandlerMappi ...
- 杂项:WWW
ylbtech-杂项:WWW WWW是环球信息网的缩写,(亦作“Web”.“WWW”.“'W3'”,英文全称为“World Wide Web”),中文名字为“万维网”,"环球网"等 ...
- 学习:Dom4j和Xpath
1.DOM4J简介 DOM4J是 dom4j.org 出品的一个开源 XML 解析包.DOM4J应用于 Java 平台,采用了 Java 集合框架并完全支持 DOM,SAX 和JAXP. DOM4J使 ...
- Codeforces-591C题解
一.题目链接 http://codeforces.com/problemset/problem/591/C 二.题意 给定一个只含数字0和1的数组,通过如下方式,变成不再变化的01组合,最少需要操作几 ...
- node的socket.io的之事件篇
socket.io类库不但可以相互发送消息,而且还可以通过socket端口对象的emit方法互相发送事件. emit在之前的事件上说过现在一句话带过:emit是用来手动触发事件的. socket.em ...