内存泄漏检测必须用工具定位具体行号,预防需依赖RAII和智能指针;Valgrind的definitely lost最明确,ASan更适合日常开发,二者不可共存。

直接说结论:内存泄漏检测不能只靠“看”,得用工具定位到具体行号;预防不能只靠“记得 free”,得靠 RAII 和智能指针把释放逻辑绑定到生命周期上。
Valgrind 报 definitely lost 是最明确的泄漏信号
看到 Valgrind 输出里有 definitely lost,基本可以断定是 new/malloc 后彻底丢了指针——比如函数返回前没 delete、异常跳过释放路径、或指针被覆盖。这类泄漏最容易修复,但必须在 Linux 下跑,且程序得用 debug 编译(带符号表)。
-
valgrind --leak-check=full --show-leak-kinds=definite ./myapp是最小有效命令,别加多余参数干扰输出 - 如果程序一启动就崩溃,先加
--tool=memcheck确保不是越界导致的误报 - Valgrind 会卡慢 10–30 倍,别在压测环境里开着跑;日常开发阶段就该集成进 CI 流程,每次 PR 都扫一次
- 注意
still reachable不算泄漏——那是全局指针还活着,比如单例对象持有的堆内存,只要进程退出前不释放,Valgrind 就会报,但通常不用修
std::unique_ptr 要比裸指针更早介入代码逻辑
不是等发现泄漏了再改,而是写第一行 new 的时候就该决定用不用智能指针。裸指针 + 手动 delete 是高风险组合,尤其在有多个 return、throw、或 early exit 的函数里。
- 所有 new 出来的单个对象,优先写成
auto ptr = std::make_unique<t>(...)</t>;数组用std::make_unique<t>(n)</t> - 不要用
std::shared_ptr替代unique_ptr,引用计数开销和循环引用风险不值得 - 类成员变量如果是动态分配的,别存裸指针,改用
std::unique_ptr,析构自动触发,连delete都不用写 - 如果必须传裸指针给 C 接口(比如
libcurl),用ptr.release()显式移交所有权,并立刻置空原智能指针变量,避免误用
AddressSanitizer(ASan)比 Valgrind 更适合日常开发
Valgrind 是离线分析,ASan 是编译期插桩,运行时实时报错,能直接打出调用栈和源码行号,而且支持 macOS 和 Windows(Clang/MSVC)。对开发阶段快速定位泄漏特别有用。
立即学习“C++免费学习笔记(深入)”;
- 编译加
-fsanitize=address -g,链接也加同样参数;运行时报heap-use-after-free或memory leak都是真实问题 - ASan 默认不检测内存泄漏,要额外加
-fsanitize=leak(LLVM 15+ 支持),否则只报越界不报漏 - ASan 会增加约 2 倍内存占用和 2–3 倍运行开销,但比 Valgrind 轻得多,可放进单元测试流程
- 注意:ASan 和 Valgrind 不能同时用,会冲突;生产环境禁用,仅用于开发和测试
重载 operator new 只适合小范围临时验证
自己实现 operator new 记录分配位置,听起来可控,实际容易踩坑:它无法捕获 malloc、第三方库的分配、或 STL 容器内部的内存申请,覆盖率低;而且一旦忘了加 noexcept,new 失败时会抛异常,反而引入新问题。
- 只在没有构建环境权限、又急需确认某段代码是否泄漏时用,比如客户现场临时打 patch
- 记录日志别写磁盘,用内存 map 或环形缓冲区,否则 IO 会拖垮性能
- 上线前必须删掉,否则影响稳定性;别把它当成长期监控手段
- 真正需要运行时监控,用
gperftools的 heap profiler 或腾讯的tcmalloc+Tencent tMem Monitor,它们专为线上设计
最难防的泄漏,往往发生在跨模块边界的地方:A 模块 new,B 模块 delete;或者基类析构函数没加 virtual,导致派生类资源没释放。这些不会被 ASan 直接标出,得靠代码审查 + 统一内存管理规范来堵住。


















