Valgrind Memcheck 将内存泄漏按指针可达性分为四类:definitely lost(无任何指针指向,必须修复)、indirectly lost(由definitely lost块内指针引发,随根因修复自动消失)、possibly lost(多为指针偏移误判)和still reachable(如全局变量,通常无需处理)。

Valgrind Memcheck 报告不是日志,是内存状态快照;直接扫“definitely lost”行就能定位主因,但必须结合调用栈和源码上下文才能确认是否真漏。
怎么看懂 definitely lost 和 indirectly lost 的区别
Memcheck 把泄漏按指针可达性分四类,真正要优先处理的只有 definitely lost:它表示内存块已分配、且没有任何有效指针指向它——基本等于“彻底丢弃”。
indirectly lost 是它的连带结果:某个 definitely lost 块里存着另一个堆地址,那个地址指向的内存也被标为间接丢失。它不独立存在,删掉根因就自动消失。
possible lost 多半是容器或复杂指针结构(如 std::map 里存了裸指针)导致的误判,still reachable 通常是全局/静态对象或 main 结束前没释放的资源,这两类一般不用修。
立即学习“C++免费学习笔记(深入)”;
- 只修复
definitely lost对应的分配点,其他类型会自然收敛 - 一个报告里出现多个
definitely lost,说明有多个独立泄漏点,别只 fix 第一个 - 如果
indirectly lost字节数远大于definitely lost,说明泄漏源头可能是个大对象(比如 vector 或 map),里面塞了一堆小指针
--track-origins=yes 什么时候必须开
这个参数会让 Valgrind 记录每个未初始化值的来源,但它会让运行慢 2–3 倍、内存多占 30%。对纯泄漏问题,它几乎没用——泄漏不依赖值是否初始化,而依赖指针是否被覆盖或作用域结束。
但它在以下场景关键:
- 报告里出现
Invalid read of size X或Use of uninitialised value,且堆栈指向库函数(如memcpy、std::string::assign)时,必须开它才能追到你代码里哪一行传了野值 - 泄漏发生在模板实例化深处(比如
std::vector<T>::push_back内部 new),而堆栈只显示 STL 符号,开它可能暴露出你传入的原始指针在哪被赋值为 nullptr 或重用 - 你怀疑是
placement new或自定义分配器搞乱了元数据,它能帮你确认某块内存从分配起就没被正确初始化过
为什么 -g 编译选项不能省,但 -O0 有时反而干扰判断
-g 让 Valgrind 能把地址映射回 file.cpp:line,没它只能看到汇编偏移或符号名(比如 _ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKT1_),根本没法 debug。
-O0 关优化确实能让堆栈更“直”,但某些泄漏恰恰藏在优化行为里:比如编译器把局部指针变量优化掉,导致本该在作用域末尾 delete 的指针提前不可达;或者内联函数让分配/释放不在同一帧,-O0 下堆栈反而掩盖了真实控制流。
- 先用
-g -O2跑一次,拿到泄漏位置;再切到-g -O0确认逻辑——如果两者报告位置不一致,大概率是优化暴露了你代码里的生命周期 bug - 如果
-O0下完全不报泄漏,而-O2下稳定触发,重点检查:局部智能指针是否被 move 出作用域、lambda 捕获是否用了值捕获却忘了析构、RAII 对象是否依赖特定构造顺序 - 调试时临时加
asm volatile("" ::: "r0", "r1")防止寄存器复用,比关优化更精准
报告里出现 malloc 但代码里全是 new,怎么回事
C++ 的 new 默认底层调 malloc,所以 Memcheck 统一记作 malloc 分配。但如果你看到报告里写 operator new,说明编译器生成了标准 new 表达式符号;如果只写 malloc,可能是:
- 用了
std::allocator::allocate或std::pmr::polymorphic_allocator,它们绕过 operator new 直接调 malloc - 链接了第三方库(如 Boost.Container),其容器内部用 malloc 管理内存
- 你自己重载了全局
operator new,但实现里调了 malloc 而没打日志,Valgrind 只能看到 malloc 层
此时别盯着 “malloc” 字眼,看堆栈最上层的你自己的源码行号——那才是泄漏发生点。如果堆栈全在 libc 或 libstdc++ 里,说明泄漏来自你传给标准库的回调(比如 std::sort 的比较函数里 new 了东西没删)。


















