用valgrind查内存泄漏前需确保程序能正常运行,否则易因ASLR、fork+mmap、缺少-g、prctl禁用dumpable、动态库路径错误等原因失败;new/delete必须严格配对;shared_ptr循环引用、全局对象析构顺序、atexit不安全调用等亦是常见泄漏源。

用 valgrind 查内存泄漏前,先确认程序能跑起来
很多同学一上来就跑 valgrind --leak-check=full ./a.out,结果报错说“cannot execute binary file”或直接段错误——八成是没关 ASLR 或程序本身带 fork+mmap 混合使用,valgrind 跟踪失败。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 确保编译时加
-g(否则valgrind只显示地址,不显示行号) - 避免在程序里调用
prctl(PR_SET_DUMPABLE, 0),它会让valgrind无法注入 - 如果用了
std::thread或第三方库(如 Boost.Asio),先简化到单线程最小可复现版本再测 - Linux 下若程序依赖
.so且路径不在LD_LIBRARY_PATH中,valgrind会静默失败,记得提前export LD_LIBRARY_PATH=...
new 和 delete 不配对是最常见的泄漏源头
不是语法错误,编译器几乎不报,但后果严重:比如 new int[10] 用 delete p(漏了 []),C++ 标准规定行为未定义,常见表现是析构函数不调用、后续 malloc 分配变慢、甚至 free 崩溃。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 所有
new T必须配delete;所有new T[n]必须配delete[]—— 这条没有例外 - 禁止在构造函数里抛异常后手动
delete已分配的成员:改用std::unique_ptr自动管理 - 如果必须裸指针(如对接 C 接口),把
new/delete写在同一作用域,或用注释显式标记配对关系,例如:// new here → delete at line 87
RAII 不是银弹:std::shared_ptr 循环引用照样泄漏
用 std::shared_ptr 就以为万事大吉?错。两个对象互相持有一个 std::shared_ptr,引用计数永远不归零,内存就卡死了——valgrind 会报 “still reachable”,容易被误判为安全。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 父子关系中,子对象持有父对象的
std::weak_ptr,而非std::shared_ptr - 用
std::enable_shared_from_this时,确保对象已由std::shared_ptr管理后再调shared_from_this(),否则抛std::bad_weak_ptr - 调试时可在关键类里加静态计数器,打印构造/析构次数,快速验证是否真释放
全局变量和静态局部变量的析构顺序不可控
全局 std::vector<:shared_ptr>></:shared_ptr> 在 main 结束后才析构,但如果它依赖某个静态库里的单例(比如日志模块),而该单例又早于 vector 析构,那 vector 里对象的析构函数里调日志就会 crash——表面像内存泄漏,其实是未定义行为导致的提前终止,valgrind 来不及报告剩余内存。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 避免全局容器存裸指针或智能指针;改用函数内静态局部变量 + 返回引用(保证首次访问才构造,且析构顺序与定义顺序一致)
- 如果必须用全局对象,确保其类型不含外部依赖(比如不调其他全局对象的方法)
- 用
atexit()注册清理函数时,注意它不能安全调用std::cout或任何可能分配内存的 STL 函数
真正难缠的泄漏往往不出现在 new 那一行,而出现在“谁该负责释放”这个判断模糊的地方——尤其是跨模块、跨线程、或封装在模板里的资源获取。











