【问题分析】使用gperftools分析排查内存问题

背景

当程序长时间允许时(压测、服务器程序),就会面临更大的挑战,其中内存泄漏就是一类典型的问题,内存泄漏往往不易发现,导致的现象更是千奇百怪,本文主要介绍如何借助gperftools分析一个模块的内存泄漏

案例代码

#include <iostream>
#include <thread>
#include <cstring>
#include <chrono>

constexpr int kMallocSize = 1024*1024; // 1Mb

void func1() {
    void* p = malloc(kMallocSize);
    memset(p, 1, kMallocSize);
    free(p);
}

void func2() {
    void* p = malloc(kMallocSize);
    memset(p, 1, kMallocSize);
    //free(p);
}

int main() {

std::thread t1([](){
    while(true) {
        func1();
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
});

std::thread t2([](){
    while(true) {
        func2();
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
});

if(t1.joinable()) {
    t1.join();
}

if(t2.joinable()) {
    t2.join();
}

return 0;
}

很简单的一个程序,有两个线程每个1s执行一次任务,这个任务中会创建1M的内存,在线程2的任务中忘记释放了,这样这个程序长时间运行就会产生内存泄漏。
效果:每1s内存增加1M(观察下面top的RES列内容)

mem_leak内存增长

实际情况中,内存泄漏往往会藏在某个角落,很难通过阅读代码发现,特别是现在的项目代码量都很大

使用gperftools分析定位

gperftools介绍及安装

gperftools 是一组性能分析和内存优化工具,集合中最为人们所知的可能是它的 CPU 分析器(Profiler)和堆分析器(Heap Profiler)。下面我将介绍如何使用 gperftools 中的 CPU 分析器来对 C/C++ 程序进行性能分析。
安装 gperftools,你可以根据你所使用的操作系统和包管理器选择不同的方法。以下是在一些常见环境中安装 gperftools 的指南:

在 Ubuntu/Debian 系统上

使用 apt 包管理器安装:

sudo apt update
sudo apt install google-perftools libgoogle-perftools-dev

这将会安装 gperftools 及其开发库,如果你想要链接 CPU 分析器(profiler)到你的应用程序中,通常需要这些开发库。

在 CentOS/RHEL 系统上

使用 yum 包管理器安装:

sudo yum install gperftools gperftools-libs gperftools-devel

或者,如果你在使用新版本的 RHEL/CentOS(例如 RHEL 8 或 CentOS Stream),你可能需要使用 dnf:

sudo dnf install gperftools gperftools-libs gperftools-devel

在 Fedora 系统上

使用 dnf 包管理器安装:

sudo dnf install gperftools gperftools-libs gperftools-devel

在 macOS 上

如果你在 macOS 中,可以使用 Homebrew 来安装 gperftools:

brew install gperftools

从源代码编译安装

如果你的系统上没有预打包的 gperftools 版本,或者你需要一个特定版本的 gperftools,你还可以从源代码编译安装。首先,你需要下载最新版的源代码:

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

然后编译并安装:

./configure
make
sudo make install

注意,编译 gperftools 可能需要额外的依赖项,如 libunwind、autoconf、automake 和 libtool 等。

使用堆分析器来分析内存分配情况

详细使用方法可以参考github说明文档

  1. 准备
    pprof是否安装成功
[root@yms:/mnt/data/yms/study]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.

tcmalloc.so的位置

[root@yms:/mnt/data/yms/study]find /usr/ -name libtcmalloc.so
/usr/lib/x86_64-linux-gnu/libtcmalloc.so
  1. 启动堆分析器
    设置LD_PELOAD和HEAPPROFILE,然后运行程序
LD_PRELOAD="/usr/lib/x86_64-linux-gnu/libtcmalloc.so" HEAPPROFILE=/tmp/mem_leak ./gperftools_demo/mem_leak

注意LD_PRELOAD是必须的

其他控制配置:

配置作用
LD_PRELOAD指定用于libtcmalloc.so的路径
HEAPPROFILE指定生成heap-profiling文件的路径
HEAP_PROFILE_ALLOCATION_INTERVAL指示heap dump大小,单位Byte,即每次申请达到N Byte后dump一次,default: 1073741824(1Gb)。也可以指定其他参数
HEAP_PROFILE_INUSE_INTERVAL每增加N Byte dump一次。默认值104857600 (100 Mb)
HEAP_PROFILE_TIME_INTERVAL时间间隔,每隔N Seconds dump一次
HEAP_PROFILE_BUFFER_SIZE设置dump出来的heap文件的最大size。(gperftools源代码中无此参数,代码中的buffer默认为1M,经测试1M的情况下会丢弃不少数据,故建议设置100M(104857600B)
HEAPPROFILESIGNAL可设置dump一次heap文件的信号量(尽量挑选没有被使用到的信号量,比如SIGUSR1(10)和SIGUSR2(12))。
  1. 分析堆数据
    使用pprof查看数据分析结果:
    1. 文本方式查看, 可以明确的看出,内存泄漏在func2上
    [root@yms:/mnt/data/yms/study]pprof --text build/gperftools_demo/mem_leak /tmp/mem_leak.0001.heap 
    Using local file build/gperftools_demo/mem_leak.
    Using local file /tmp/mem_leak.0001.heap.
    Total: 100.0 MB
        99.0  99.0%  99.0%     99.0  99.0% func2
         1.0   1.0% 100.0%      1.0   1.0% func1
         0.0   0.0% 100.0%      0.0   0.0% allocate_dtv
         0.0   0.0% 100.0%      0.0   0.0% std::thread::_S_make_state
         0.0   0.0% 100.0%    100.0 100.0% __GI___clone
         0.0   0.0% 100.0%      0.0   0.0% __libc_start_main
         0.0   0.0% 100.0%      0.0   0.0% __pthread_create_2_1
         0.0   0.0% 100.0%      0.0   0.0% _start
         0.0   0.0% 100.0%      0.0   0.0% allocate_stack (inline)
         0.0   0.0% 100.0%      0.0   0.0% main
         0.0   0.0% 100.0%      1.0   1.0% main::{lambda#1}::operator
         0.0   0.0% 100.0%     99.0  99.0% main::{lambda#2}::operator
         0.0   0.0% 100.0%    100.0 100.0% start_thread
         0.0   0.0% 100.0%      1.0   1.0% std::__invoke@27bf
         0.0   0.0% 100.0%     99.0  99.0% std::__invoke@28e5
         0.0   0.0% 100.0%    100.0 100.0% std::__invoke_impl
         0.0   0.0% 100.0%    100.0 100.0% std::error_code::default_error_condition
         0.0   0.0% 100.0%    100.0 100.0% std::thread::_Invoker::_M_invoke
         0.0   0.0% 100.0%    100.0 100.0% std::thread::_Invoker::operator
         0.0   0.0% 100.0%      0.0   0.0% std::thread::_M_start_thread
         0.0   0.0% 100.0%    100.0 100.0% std::thread::_State_impl::_M_run
         0.0   0.0% 100.0%      0.0   0.0% std::thread::thread
    
    1. 转换成pdf
      首先要安装graphviz
    	 sudo apt install graphviz
    
    然后转换成pdf
    [root@yms:/mnt/data/yms/study]pprof --pdf build/gperftools_demo/mem_leak /tmp/mem_leak.0001.heap > mem_leak.pdf
    Using local file build/gperftools_demo/mem_leak.
    Using local file /tmp/mem_leak.0001.heap.
    Dropping nodes with <= 0.5 MB; edges with <= 0.1 abs(MB
    
    打开pdf
    在这里插入图片描述
  2. 转换成网页
pprof --web ./my_program /tmp/my_heap_profile.0001.heap

思考

好的工具,可以事半功倍,要持续学习使用新工具,人类和动物最大的区别就是学会使用了工具

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值