使用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选 ...
随机推荐
- 【C#】datetimepicker初始为空值的方法
方法一: 在窗口初始化函数中添加: dateTimePickerEnd.Format = DateTimePickerFormat.Custom; dateTimePickerEnd.CustomFo ...
- FastAdmin 前端页面传参笔记
FastAdmin 前端页面传参笔记 看到 QQ 群里的小伙伴询问如何传参,然后在社区里找到一笔记帖子 1 还要参考在线文档控制器部分2. 引用 Karson 的回复: 如果我们需要自己在控制器中透传 ...
- Linux 之 hugepage 大页内存理论
HugePages是通过使用大页内存来取代传统的4kb内存页面,使得管理虚拟地址数变少,加快了从虚拟地址到物理地址的映射以及通过摒弃内存页面的换入换出以提高内存的整体性能.尤其是对于8GB以上的内存以 ...
- Zookeeper的shell操作
一.客户端连接服务器 zkCli.sh start 二.命令操作 进入到客户端操作行,键入help 查看zookeeper命令列表 常用命令 1) 查看节点列表:ls 路径 2) 创建节点:creat ...
- C# Socket Post File
///<summary> ///向服务器发送混合型的请求,1:成功发送,0:发送失败 ///</summary> ///<param name="paranam ...
- Web Service简介(一)
这篇博文,我们对Web Service进行一个简单的介绍和认识,作为Web Service的入门.在学习之前,你需要对HTML和XML有基本的了解,Web Service并不难,而且非常的简单. 什么 ...
- 安装MySQL-python 的问题
安装MySQL-python 的问题 1.CentOS下载mysql-devel安装 yum install mysql-devel 2.Ubuntu下不叫mysql-devel,而是叫libmysq ...
- zufeoj NO.1(结构体简单题)
NO.1 时间限制: 1 Sec 内存限制: 128 MB提交: 457 解决: 172[提交][状态][讨论版] 题目描述 所谓NO.1,就是所有成绩都排在第一的同学,我们假设每个人只有理科,文 ...
- 淘宝开源Web服务器Tengine基本安装步骤
Tengine 是由淘宝核心系统部基于Nginx开发的Web服务器,它在Nginx的基础上,针对大访问量 网站的需求,添加了很多功能和特性.Tengine的性能和稳定性已经在大型的网站如淘宝网,淘宝商 ...
- mongoDB在windows下安装和配置.
1.首先在官网下载mongoDB的安装包: http://www.mongodb.org/downloads 这里我们下载zip格式的下载,其他的没安装过,不会,就不说了. 2.解压文件后: 3.在D ...