使用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选 ...
随机推荐
- 《DSP using MATLAB》示例Example 8.25
- 《selenium2 python 自动化测试实战》(19)——webdriver错误截图
webdriver 提供错误截图函数 get_screenshot_as_file(),可以帮助我们跟踪 bug,在脚本无法继续执行时候, get_screenshot_as_file()函数将截取当 ...
- 异步加载JS几种方式
默认情况javascript是同步加载的,也就是javascript的加载时阻塞的,后面的元素要等待javascript加载完毕后才能进行再加载,对于一些意义不是很大的javascript,如果放在页 ...
- dockerize 容器工具集基本使用
基本功能: * 在启动的时候根据环境变量或者模版生成配置文锦啊 * 多日志文件重定向到标准输入输出 * 等待其他服务(tcp,http unix)起来之后在启动主进程 1. 安装 直 ...
- VMware harbor && minio 搭建企业docker私有镜像以及需要注意的问题
1. docker harbor 配置 建议使用在线安装的模式(离线包太大了) 首先需要安装docker-compose .docker .mino (具体安装可以参考官网后者我的博 ...
- sublime设置右键在浏览器打开
用sublime编辑html文件,右键此文件,自带一个在浏览器中打开,但是这个是在IE里打开, 如何在chrome里打开呢. 1. 这里插入一下安装"view in browser" ...
- MySQL的安装配置教程
1. 官网下载ZIP压缩版本(本人电脑是64位的) x64bit MySQL Community 2. 解压到E:\SoftwareFiles\mysql-5.7.11-winx64 3. 在E:\S ...
- [LeetCode系列] 二叉树最大深度求解问题(C++递归解法)
问: 给定二叉树, 如何计算二叉树最大深度? 算法描述如下: 如果当前节点为空, 返回0(代表此节点下方最大节点数为0) 如果当前节点不为空, 返回(其左子树和右子树下方最大节点数中的最大值+1) 上 ...
- YARN的Fair Scheduler和Capacity Scheduler
关于Scheduler YARN有四种调度机制:Fair Schedule,Capacity Schedule,FIFO以及Priority: 其中Fair Scheduler是资源池机制,进入到里面 ...
- PHP应用的CI/CD流程实践与学习:一、PHP运行环境的准备
前言:一直以来想学习与实践一下敏捷开发,之前项目虽说口口声声我们项目是敏捷开发,其实很扯. 敏捷开发如果有持续集成.持续部署的支持,那样开发.测试.运维将节省不少精力. 此系列博客只为记录CI/CD的 ...