LeakSanitizer必须与AddressSanitizer联用,因LSan是libasan内部组件而非独立库;单独使用-fsanitize=leak无效,需配合-fsanitize=address及ASAN_OPTIONS=detect_leaks=1。

LeakSanitizer 不是独立工具,它必须与 AddressSanitizer(ASan)一起编译并运行;单独启用 -fsanitize=leak 无效,链接时会报错或静默忽略泄漏检测。
为什么 -fsanitize=leak 单独不工作
LSan 是 LLVM/Clang 中 ASan 运行时库的一部分,不是可单独加载的 sanitizer。即使你写 -fsanitize=leak,Clang 实际仍会链接 libasan,且仅当 ASAN_OPTIONS=detect_leaks=1(默认开启)时才激活泄漏扫描。GCC 的 LSan 支持更弱,仅在较新版本(≥9)中作为 ASan 的子功能存在,且依赖 libasan。
-
-fsanitize=leak在 Clang 中只是个“占位符”,不触发独立链接行为 - GCC 8 及更早版本完全不支持 LSan;GCC 9+ 需同时启用
-fsanitize=address - 没有
liblsan.so独立库 —— 所有逻辑都在libasan内部,通过环境变量开关
正确启用 LSan 的编译与运行命令
以 Clang 为例,必须启用 ASan 并确保泄漏检测未被禁用:
clang++ -g -O1 -fsanitize=address -fno-omit-frame-pointer main.cpp ASAN_OPTIONS=detect_leaks=1 ./a.out
关键点:
立即学习“C++免费学习笔记(深入)”;
-
-fno-omit-frame-pointer强烈建议加上,否则堆栈追踪可能为空或错乱 -
-O1是推荐优化级:更高(如-O2)可能导致内联掩盖泄漏源头 -
detect_leaks=1是默认值,但显式写出更可靠(尤其 CI 环境中 ASAN_OPTIONS 可能被覆盖) - 若程序调用
exit()或 _Exit(),LSan 可能来不及报告 —— 应让main()正常返回
常见误报、漏报与绕过场景
LSan 不是万能的,它只扫描进程退出时仍可达的堆内存块,且依赖符号与调试信息:
- 全局指针指向 new 出的内存 → 被视为“仍存活”,**不算泄漏**(这是设计行为,非 bug)
- 使用
mmap(MAP_ANONYMOUS)或malloc后被dlclose卸载的共享库中分配的内存 → **可能漏报**(LSan 不扫描 dlopen 区域) - 未带
-g编译 → 泄漏报告只有地址,无文件/行号,难以定位 - 程序被
kill -9终止 → LSan 完全无机会运行,**零报告** - C++ static 对象析构函数中发生的 new → 若析构顺序导致指针丢失,可能被误判为泄漏
如何确认 LSan 实际生效
最直接方式:制造一个明确泄漏,看是否报出 indirect leak 或 direct leak:
#includeint main() { new int[100]; // 故意泄漏 return 0; // 让 LSan 有机会扫描 }
运行后应看到类似输出:
=================================================================
==12345==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 400 byte(s) in 1 object(s) allocated from:
#0 0x... in operator new[](unsigned long) .../libasan.so.5
#1 0x... in main (.../test.cpp:3)
若没输出,检查:
- 是否用了
ASAN_OPTIONS=detect_leaks=0(包括 shell 环境中残留设置) - 是否链接了
-fsanitize=address,而非仅=leak - 是否在
main返回前调用了_exit()或std::quick_exit()
LSan 的“泄漏”定义严格依赖程序终止时的可达性分析,不是所有未 delete 的内存都会报 —— 它只报那些既没被栈/寄存器/全局变量引用,又没被其他活对象间接持有的内存。这点容易误解,但恰恰是它低误报率的基础。









