跳转至

💡 一分钟速览:用可复现示例介绍 Valgrind Massif 的运行命令、ms_print 峰值快照与分配树,以及堆内存测量不包含的内容。

最后更新:2026-10-13

Valgrind Massif:定位 C++ 堆内存峰值

Massif 在程序运行期间记录堆使用量,将快照写入 massif.out.<pid>;ms_print 把快照整理成图和分配调用树。它适合回答**内存何时达到峰值、哪些分配路径贡献最大**,但不能单凭这个报告认定发生内存泄漏。Valgrind 通常在 Linux 上使用;下方命令假设该环境已安装 Valgrind。

一个可复现的小程序

#include <cstddef>
#include <vector>

int main() {
    std::vector<char> first(1'000'000, 'a');
    std::vector<char> second(2'000'000, 'b');
    volatile std::size_t live = first.size() + second.size();
    return live == 3'000'000 ? 0 : 1;
}

带调试符号编译,再运行分析器:

c++ -std=c++17 -g -O0 massif_demo.cpp -o massif_demo
valgrind --tool=massif --time-unit=B --massif-out-file=massif.out.%p ./massif_demo
ms_print massif.out.* | less

短程序使用 --time-unit=B 时,横轴表示分配/释放的字节数,而不是默认的指令数;它**不会**改变纵轴的内存单位。输出文件名包含进程 ID,多次运行时应选择对应文件。分析实际程序时保留调试符号,更容易识别分配调用栈。

如何读报告

  1. 找到标为 peak 的快照,读取总字节数。图展示运行过程中的使用变化,不等同于操作系统统计的 RSS。
  2. 查看峰值下方的分配调用树。它把请求的堆字节及分配器开销归到调用路径;本例两个向量应通过分配函数出现。
  3. 修改一处代码后比较等价运行。输入、编译选项、分配器和分析参数应一致。Valgrind 会显著拖慢运行,因此这里不能用来做速度基准。

默认只分析堆,栈分析是关闭的;只有需要研究栈使用量时才加 --stacks=yes。--detailed-freq=1 会增加详细快照与分析开销,可在默认详情不足时使用。自定义分配器、内存映射区域等需要额外解释;不要把报告总量直接当作进程全部内存。详见 Massif 官方手册。

原项目中的用法

项目曾在某个工作负载中通过 gpstart 包装运行;其参数依赖该项目的运行环境,因此建议先使用上面的独立 valgrind 命令。下方截图展示旧工作流和 ms_print 报告,仅作为界面示例,不代表你的机器会得到相同数值。

此前的 Massif 分析工作流

ms_print 堆内存报告示例

另一个基准测试误区可读查表法与基准陷阱;若要检测非法内存访问,可读 AddressSanitizer。

相关主题

评论