使用 gperftools 检测内存泄露

2023-12-06 04:50

本文主要是介绍使用 gperftools 检测内存泄露,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

每个 C/C++ 程序员可能都经历过定位内存泄露问题的痛苦。一方面,为了减少内存泄露问题,在 C++ 程序中,我们应该尽量使用智能指针,在 C 程序中,我们也可以通过一些内存池技术来管理内存的申请与释放。另一方面,当内存泄露问题真的出现时,通过 gperftools 的 heap checker,我们也可以比较轻松地找到内存泄露的线索,缩短问题定位时间。

一、gperftools 简介

gperftools 是 google 开源的一组套件,提供了高性能的、支持多线程的 malloc 实现,以及一组优秀的性能分析工具。

二、安装 gperftools

2.1、下载源码

从 gperftools github 官网上下载最新版本的源码包:

wget https://github.com/gperftools/gperftools/releases/download/gperftools-2.9.1/gperftools-2.9.1.tar.gz

2.2、解压源码包

tar -zxv -f gperftools-2.9.1.tar.gz

2.3、configure

cd gperftools-2.9.1
./configure

命令结束执行后出现一个报错:

configure: WARNING: No frame pointers and no libunwind. Using experimental backtrace capturing via libgcc. Expect crashy cpu profiler.

这是因为没有安装 libunwind。这里直接使用 yum 的方式安装:

yum install libunwind-devel

再次执行 ./configure,命令执行成功。

2.4、编译并安装

执行如下两个命令,进行编译并安装:

make
sudo make install

最后执行 ldconfig 更新动态库文件

2.5、确认安装成功

执行如下命令,确认 gperftools 安装成功

[root@36eab106d3bf gperftools-2.9.1]# pprof --version
pprof (part of gperftools 2.0)Copyright 1998-2007 Google Inc.This is BSD licensed software; see the source for copying conditions
and license information.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.

三、内存泄露检测

gperftools 的 heap checker 组件可以用于检测 C++ 程序中的内存泄露问题。要使用 heap checker,总共分 3 步:

  • 链接 tcmalloc 库到应用程序中
  • 运行程序
  • 分析输出
3.1、链接 tcmalloc 库

heapchecker 是 tcmalloc 的一部分,所以为了在可执行程序中使用 heap checker,在应用程序的链接阶段使用 -ltcmalloc 链接 tcmalloc 库。

3.2、运行代码

使用 heap checker 的推荐方式是 完整程序运行模式,此时 heap checker 可以在程序的 main 函数开始前跟踪内存分配,然后在程序退出时再次检查。如果它发现来了任何内存泄露,heap checker 会直接终止程序(通过 exit(1)),并打印一条消息,告诉你接下来如何使用 pprof 来继续跟踪该内存泄露问题。

完整程序运行heap checker 支持 4 种模式:

  • minimal:在程序初始化期间(即 main 函数运行前)不进行内存泄露检查
  • normal:正常模式,通过跟踪某块内存是否可以被 live object 来访问,来判断是否出现内存泄露
  • strict:类似于 normal 模式,但是对全局对象的内存泄露有一些额外的检查
  • draconian:在该模式下,只有所有申请的内存都被释放,才认为没有出现内存泄露

一般使用 normal 模式就可以满足日常要求了。

heap checker 的另一种使用方法是检测指定代码块是否出现了内存泄露。为了实现这一点,需要在代码片段的开始部分创建一个 HeapLeakChecker 结构体,并在结束部分调用 NoLeaks()。例如:

HeapLeakChecker heap_checker("test_foo");
{code that exercises some foo functionality;this code should not leak memory;
}
if (!heap_checker.NoLeaks()) assert(NULL == "heap memory leak");

需要注意,添加 HeapLeakChecker 只是在程序中添加了内存泄露检测代码,为了真实地检测程序是否出现了内存泄露,仍然需要运行程序,并打开 heap-checker。

env HEAPCHECK=local your_program

除了指定为 local 模式外,之前的 normal 等模式也是可以的,此时除了运行 local 检查外,还将进行 完整程序运行 检查。

当然,运行 heap checker 是有代价的。heap checker 需要记录每次内存申请时的调用栈信息,这就导致了使用 heap checker 时,程序需要消耗更多的内存,同时程序运行速度也更慢。另外,需要注意,由于 heap checker 内部使用了 heap profile 框架,所以不能同时运行 heap checkerheap profile

3.3、忽略已知的内存泄露

对于已知的内存泄露,如果想让 heap checker 忽略这些内存泄露信息,可以在应用程序代码中添加中如下代码:

{HeapLeakChecker::Disabler disabler;<leaky code>
}

另一种方式是使用 IgnoreObject,它接收一个指针参数,对该参数所指向的对象将不再进行内存泄露检查。

3.4、使用 pprof 查看内存泄露结果

heap checker 运行结束时会打印基本的泄露信息,包括调用栈和泄露对象的地址。除此之外,还可以使用 pprof 命令行工具来可视化地查看调用栈。

四、示例

接下来通过一个示例讲述如何使用 gperftools 的 heap checker 来发现程序的内存泄露问题。

4.1、示例程序如下:
// Copyright (C) fuchencong.com#include <iostream>int func() {int *p = new int(10);return 0;
}int main() {std::cout << "memory leak test" << std::endl;return func();
}
4.2、编译程序,并链接 tcmalloc 库:

g++ -std=c++0x -g -o memory_leak memory_leak.cpp -ltcmalloc

4.3、运行程序
[root@36eab106d3bf gperftools-test]# env HEAPCHECK=normal ./memory_leak
WARNING: Perftools heap leak checker is active -- Performance may suffer
memory leak test
Have memory regions w/o callers: might report false leaks
Leak check _main_ detected leaks of 4 bytes in 1 objects
The 1 largest leaks:
*** WARNING: Cannot convert addresses to symbols in output below.
*** Reason: Cannot find 'pprof' (is PPROF_PATH set correctly?)
*** If you cannot fix this, try running pprof directly.
Leak of 4 bytes in 1 objects allocated from:@ 4008ff@ 400935@ 7fc9f81a6555@ 400829If the preceding stack traces are not enough to find the leaks, try running THIS shell command:pprof ./memory_leak "/tmp/memory_leak.16582._main_-end.heap" --inuse_objects --lines --heapcheck  --edgefraction=1e-10 --nodefraction=1e-10 --gvIf 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
If the leak report occurs in a small fraction of runs, try running with TCMALLOC_MAX_FREE_QUEUE_SIZE of few hundred MB or with TCMALLOC_RECLAIM_MEMORY=false, it might help find leaks m
Exiting with error code (instead of crashing) because of whole-program memory leaks

这里虽然检测出了内存泄露,但是并没有打印出调用栈的符号信息,根据提示,可能是 PPROF_PATH 环境变量没有正确设置。按照如下方式设置环境变量:

# echo $PPROF_PATH
# which pprof
/usr/local/bin/pprof
# export PPROF_PATH=/usr/local/bin/pprof

再次运行,可以看到已经打印出了内存泄露的栈信息:

[root@36eab106d3bf gperftools-test]# env HEAPPROFILE=./heap_perf HEAPCHECK=normal ./memory_leak
WARNING: Perftools heap leak checker is active -- Performance may suffer
memory leak test
Have memory regions w/o callers: might report false leaks
Leak check _main_ detected leaks of 4 bytes in 1 objects
The 1 largest leaks:
Using local file ./memory_leak.
Leak of 4 bytes in 1 objects allocated from:@ 4008ff func@ 400935 main@ 7f39d94db555 __libc_start_main@ 400829 _startIf the preceding stack traces are not enough to find the leaks, try running THIS shell command:pprof ./memory_leak "/tmp/memory_leak.16586._main_-end.heap" --inuse_objects --lines --heapcheck  --edgefraction=1e-10 --nodefraction=1e-10 --gvIf 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
If the leak report occurs in a small fraction of runs, try running with TCMALLOC_MAX_FREE_QUEUE_SIZE of few hundred MB or with TCMALLOC_RECLAIM_MEMORY=false, it might help find leaks m
Exiting with error code (instead of crashing) because of whole-program memory leaks

但是这个信息展现方式并没有直接指出问题产生的行数。我们可以使用其提示的指令,调用可视化工具

pprof ./memory_leak ./heap_perf.0001.heap --inuse_objects --lines --heapcheck  --edgefraction=1e-10 --nodefraction=1e-10 --gv
4.4、错误解决办法
4.4.1、gv错误
gv: Unable to open the display.

说明无法打开显示器,也就是说,–gv选项,需要在带图形界面的系统上使用,我们可以用–svg选项,将生成svg,然后在浏览器打开,命令:

pprof ./memory_leak ./heap_perf.0001.heap --inuse_objects --lines --heapcheck  --edgefraction=1e-10 --nodefraction=1e-10 --svg > svg.svg
4.4.2、addr2line错误

/usr/bin/addr2line: DWARF error: could not find variable specification at offset 5fc4
/usr/bin/addr2line: DWARF error: could not find variable specification at offset 60c0
/usr/bin/addr2line: DWARF error: could not find variable specification at offset 6137

可以在编译的时候将-g换成-pg,例如

g++ -std=c++0x -pg -o memory_leak memory_leak.cpp -ltcmalloc

4.4.3、dot错误

sh: dot: command not found

安装graphviz

yum install graphviz

在这里插入图片描述

五、线上内存泄露检测

5.1、demo

文件夹中的四个文件

BUILD
WORKSPACE
memory_leak.cpp
start.sh
.bazelrc

BUILD文件

cc_binary(name = "memory_leak",linkopts = ["-ltcmalloc",],srcs = ["memory_leak.cpp"],
)

-ltcmalloc必须链接上

memory_leak.cpp文件

#include <iostream>int func() {int *p = new int(10);return 0;
}int main() {std::cout << "memory leak test" << std::endl;return func();
}

start.sh文件

export PPROF_PATH=/usr/local/bin/pprof
nohup env HEAPPROFILE=./heap_perf HEAPCHECK=normal ./memory_leak 2>&1 | tee  "./log.log">> "./log.log" 2>&1 & 

.bazelrc文件

build --copt=-O2
build --copt=-g
build --cxxopt=-std=c++17

WORKSPACE文件为空文件

编译命令

bazel build :memory_leak

如果在生成svg过程中,出现4.4.2错误,将.bazelrc文件中的-g换成-pg

六、参考

https://fuchencong.com/2021/04/22/develop-tools-1/

https://cloud.tencent.com/developer/article/1383795

这篇关于使用 gperftools 检测内存泄露的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/460501

相关文章

Nginx使用Keepalived部署web集群(高可用高性能负载均衡)实战案例

《Nginx使用Keepalived部署web集群(高可用高性能负载均衡)实战案例》本文介绍Nginx+Keepalived实现Web集群高可用负载均衡的部署与测试,涵盖架构设计、环境配置、健康检查、... 目录前言一、架构设计二、环境准备三、案例部署配置 前端 Keepalived配置 前端 Nginx

Python logging模块使用示例详解

《Pythonlogging模块使用示例详解》Python的logging模块是一个灵活且强大的日志记录工具,广泛应用于应用程序的调试、运行监控和问题排查,下面给大家介绍Pythonlogging模... 目录一、为什么使用 logging 模块?二、核心组件三、日志级别四、基本使用步骤五、快速配置(bas

使用animation.css库快速实现CSS3旋转动画效果

《使用animation.css库快速实现CSS3旋转动画效果》随着Web技术的不断发展,动画效果已经成为了网页设计中不可或缺的一部分,本文将深入探讨animation.css的工作原理,如何使用以及... 目录1. css3动画技术简介2. animation.css库介绍2.1 animation.cs

使用雪花算法产生id导致前端精度缺失问题解决方案

《使用雪花算法产生id导致前端精度缺失问题解决方案》雪花算法由Twitter提出,设计目的是生成唯一的、递增的ID,下面:本文主要介绍使用雪花算法产生id导致前端精度缺失问题的解决方案,文中通过代... 目录一、问题根源二、解决方案1. 全局配置Jackson序列化规则2. 实体类必须使用Long封装类3.

Python文件操作与IO流的使用方式

《Python文件操作与IO流的使用方式》:本文主要介绍Python文件操作与IO流的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、python文件操作基础1. 打开文件2. 关闭文件二、文件读写操作1.www.chinasem.cn 读取文件2. 写

PyQt6中QMainWindow组件的使用详解

《PyQt6中QMainWindow组件的使用详解》QMainWindow是PyQt6中用于构建桌面应用程序的基础组件,本文主要介绍了PyQt6中QMainWindow组件的使用,具有一定的参考价值,... 目录1. QMainWindow 组php件概述2. 使用 QMainWindow3. QMainW

使用Python自动化生成PPT并结合LLM生成内容的代码解析

《使用Python自动化生成PPT并结合LLM生成内容的代码解析》PowerPoint是常用的文档工具,但手动设计和排版耗时耗力,本文将展示如何通过Python自动化提取PPT样式并生成新PPT,同时... 目录核心代码解析1. 提取 PPT 样式到 jsON关键步骤:代码片段:2. 应用 JSON 样式到

java变量内存中存储的使用方式

《java变量内存中存储的使用方式》:本文主要介绍java变量内存中存储的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍2、变量的定义3、 变量的类型4、 变量的作用域5、 内存中的存储方式总结1、介绍在 Java 中,变量是用于存储程序中数据

关于Mybatis和JDBC的使用及区别

《关于Mybatis和JDBC的使用及区别》:本文主要介绍关于Mybatis和JDBC的使用及区别,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、JDBC1.1、流程1.2、优缺点2、MyBATis2.1、执行流程2.2、使用2.3、实现方式1、XML配置文件

macOS Sequoia 15.5 发布: 改进邮件和屏幕使用时间功能

《macOSSequoia15.5发布:改进邮件和屏幕使用时间功能》经过常规Beta测试后,新的macOSSequoia15.5现已公开发布,但重要的新功能将被保留到WWDC和... MACOS Sequoia 15.5 正式发布!本次更新为 Mac 用户带来了一系列功能强化、错误修复和安全性提升,进一步增