leaksanitizer(lsan)是集成于asan生态的内存泄漏检测器,仅在程序退出时检查堆内存泄漏;必须显式启用(-fsanitize=leak或-fsanitize=address,leak)、禁用优化(-o0)并确保全量编译链接带调试信息,否则无法准确定位泄漏源码位置。

LeakSanitizer 编译时必须加 -fsanitize=leak 且禁用优化
LeakSanitizer(LSan)不是独立工具,而是 LLVM/Clang 和 GCC(较新版本)集成在 AddressSanitizer(ASan)生态里的内存泄漏检测器。它**只在程序退出时触发检查**,不支持运行中实时报告。要让它准确定位到代码行,必须满足两个硬性条件:
-
-fsanitize=leak必须出现在编译和链接的完整命令中(CMake 中需同时设CMAKE_CXX_FLAGS和CMAKE_EXE_LINKER_FLAGS) - 必须关闭优化(
-O0),否则内联、死代码消除会抹掉调用栈帧,导致报告只显示??或系统函数
常见错误:只加了 -fsanitize=address —— 这启用的是 ASan(检测越界、UAF),但默认**不启用 LSan**;LSan 需显式开启,或通过 -fsanitize=address,leak 同时启用(推荐)。
运行时报 ERROR: LeakSanitizer: detected memory leaks 但没行号?检查符号与链接方式
即使加了 -g -O0 -fsanitize=leak,仍可能只看到 indirectly leaked by 1 object 而无源码位置。原因通常是:
- 动态链接了未带调试信息的第三方库(如系统
libstdc++.so),导致栈回溯在进入std::vector::push_back后中断 - 使用了
-static-libstdc++但没配-g,静态链接后符号丢失 - 主程序由多个翻译单元组成,但只对部分文件加了
-fsanitize=leak(必须全量参与)
验证方法:运行时加环境变量 LSAN_OPTIONS=report_objects=1:log_threads=1,看是否多出分配大小和调用栈——如果栈里全是 malloc、operator new 而没有你的函数名,基本就是符号链断裂。
Linux 下用 LSAN_OPTIONS=stack_trace_level=2 提高调用栈深度
默认 LSan 只打印 2 层栈帧(常只到 new 操作符封装层),很难定位到业务代码。通过环境变量可强制展开:
LSAN_OPTIONS="stack_trace_level=3" ./my_program
更实用的是结合 suppressions 过滤已知安全的泄漏(如某些库的全局缓存),避免噪音干扰:
echo "leak:libcurl" > lsan.supp LSAN_OPTIONS="suppressions=lsan.supp" ./my_program
注意 suppression 规则匹配的是栈中任意函数名,不是文件路径;写太宽(如 leak:std::)会漏掉真正问题。
Clang 15+ 与 GCC 12+ 的关键差异:GCC 默认不启用 LSan 退出检查
Clang 下只要链接了 -fsanitize=leak,程序退出时自动触发检测;而 GCC 12+ 默认行为是「只注入检测逻辑,不主动报告」。必须显式设置:
LSAN_OPTIONS=detect_leaks=1 ./my_program
否则即使有泄漏,也完全静默。这是 GCC 用户最常踩的坑——编译没错、运行没报错、但其实什么都没检测。
另外,LSan 对 mmap 分配(非 malloc/new)不敏感;它专注堆内存(malloc、new、calloc 等)。如果怀疑是 mmap 泄漏,得换用 valgrind --tool=memcheck 或 perf record -e syscalls:sys_enter_mmap 手动追踪。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











