valgrind 能查 c++ new/delete 泄漏但默认不报,需启用 --leak-check=full --show-leak-kinds=all;它不检测 new[]/delete 语义错配,应依赖编译器警告;raii 对象未析构导致的“逻辑泄漏”常被归为 still reachable 而忽略。

Valgrind 能不能直接查 C++ new/delete 泄漏
能,但默认不报 new 分配后没 delete 的“典型泄漏”,得开全模式。Valgrind 默认只标记“still reachable”和“definitely lost”,而很多 C++ 对象在 main 结束前没析构(比如全局 std::vector 持有的堆内存),会被归为 still reachable —— 它不算错误,但可能掩盖真实泄漏。
实操建议:
- 运行时加
--leak-check=full --show-leak-kinds=all,否则漏掉possibly lost和still reachable类别 - 确保编译带调试信息:
g++ -g -O0,否则行号全丢,定位不到哪行new没配对 - 避免用
std::shared_ptr等智能指针包裹后还手动delete,Valgrind 会报双重释放(Invalid write)
为什么跑了 Valgrind 却没报 new[] / delete 不匹配
因为 C++ 的 new[] 和 delete(非 delete[])属于未定义行为,Valgrind 不检查语义是否匹配,只跟踪内存块的分配/释放状态。它只管“这块内存有没有被 free”,不管“是不是用错 operator delete 释放了数组”。
常见错误现象:
立即学习“C++免费学习笔记(深入)”;
-
char* p = new char[10]; delete p;→ Valgrind 不报,但程序可能崩溃或踩内存 -
int* a = new int[5]; delete[] a;→ 正确,Valgrind 认为已释放 - 混合使用 malloc/free 和 new/delete → Valgrind 会报
Mismatched free() / delete / delete []
所以别依赖 Valgrind 发现 new[]/delete 错配,靠编译器警告(-Wmismatched-new-delete)或静态分析工具更靠谱。
Linux 下跑 Valgrind 报 “Permission denied” 或卡住
多半是二进制被 strip 过、用了 musl libc、或者开了某些安全机制(如 seccomp)。Valgrind 需要读取符号表和系统调用拦截能力,缺一不可。
实操建议:
- 确认没用
strip:运行file ./a.out,输出含not stripped才行 - 别用 Alpine Linux(musl)跑 Valgrind,换 Ubuntu/Debian;musl 下 Valgrind 基本不可用
- 关闭 ASLR 临时测试:
setarch $(uname -m) -R ./a.out,再套 Valgrind,避免地址随机化干扰 - 如果程序 fork 多进程,加
--trace-children=yes,否则子进程的泄漏就漏检了
C++ RAII 对象里藏着泄漏,Valgrind 为啥不提醒
因为 RAII 对象本身(比如 std::string、std::vector)的析构函数没被调用时,它们持有的堆内存会被 Valgrind 归为 still reachable —— 它认为“程序还能访问到”,不算泄漏。但如果你忘了调 obj.~MyClass() 或对象生命周期管理出错,这部分内存其实已经“逻辑泄漏”了。
容易被忽略的地方:
- 局部静态对象的析构函数在
main返回后才执行,Valgrind 报告时机早于那一步,容易误判 - 用
std::unique_ptr但指向了栈对象(new以外的地址),释放时触发Invalid free - 自定义分配器(如
std::pmr::polymorphic_allocator)分配的内存,Valgrind 可能无法识别其释放路径
这时候得结合 --track-origins=yes 看内存来源,再倒查对象生命周期是否完整。复杂点不在工具,而在 C++ 对象销毁顺序和作用域边界是否真被你掌控住了。








