内存泄漏需主动检测而非依赖编译器;linux/macos用valgrind定位泄漏点,windows用_crtdumpmemoryleaks(),推荐智能指针替代裸指针,并可通过重载new/delete做调试统计。

内存泄漏在 C++ 中无法靠编译器自动发现,必须结合工具链主动检测;不加干预的泄漏会随运行时间推移导致进程 OOM 或行为异常。
用 valgrind 检查运行时泄漏(Linux/macOS)
这是最直接、最可靠的动态检测方式,能定位到具体分配点(new / malloc 行号)和未释放的堆块大小。
- 确保程序用
-g编译(保留调试符号),例如:g++ -g -O0 leak.cpp -o leak - 运行:
valgrind --leak-check=full --show-leak-kinds=all ./leak - 重点关注
definitely lost和indirectly lost区域——这两类是真实泄漏;still reachable通常是全局/静态指针持有,需人工判断是否合理 - 若程序使用了多线程,加
--tool=memcheck --track-origins=yes可辅助追踪指针来源
Windows 下用 _CrtDumpMemoryLeaks() 快速捕获(仅 Debug CRT)
适用于 VS 环境下的轻量级验证,但只能告诉你“有泄漏”,不能指出哪一行。
- 必须在源文件开头包含:
#include <crtdbg.h></crtdbg.h> - 在
main()返回前调用:_CrtDumpMemoryLeaks(); - 确保定义了
_CRTDBG_MAP_ALLOC(通常在#include <crtdbg.h></crtdbg.h>前加#define _CRTDBG_MAP_ALLOC) - 注意:只对
malloc/calloc/realloc生效;new运算符默认不被跟踪,需重载全局operator new并调用_malloc_dbg才能纳入统计
用智能指针替代裸指针(std::unique_ptr / std::shared_ptr)
不是“检查”手段,而是从源头减少泄漏概率的核心实践。手动 delete 容易遗漏、异常跳过或重复释放。
立即学习“C++免费学习笔记(深入)”;
-
std::unique_ptr适合独占语义:构造时接管,析构时自动delete,不可拷贝,可移动 -
std::shared_ptr适合共享所有权,但要注意循环引用——用std::weak_ptr打破闭环 - 避免混用:
new分配后交给shared_ptr管理时,必须用std::shared_ptr<t>(new T)</t>或更安全的std::make_shared<t>()</t>;直接传裸指针给shared_ptr构造函数极易引发 double-delete - 容器中存对象优先于存指针:如
std::vector<widget></widget>比std::vector<:unique_ptr>></:unique_ptr>更简单、更安全
自定义 new/delete 配合计数器(调试阶段)
当工具链受限(如嵌入式环境无 valgrind),或需要长期监控某模块的分配趋势时,可临时注入轻量级统计逻辑。
- 重载全局
operator new和operator delete,内部维护一个原子计数器 + 调用栈(可用backtrace()或__builtin_frame_address(0)) - 记录每次分配的 size、地址、调用位置(
__FILE__+__LINE__),并在程序退出前打印未匹配的分配 - 注意:重载后所有第三方库的堆分配也会被拦截,可能干扰其内部逻辑;建议仅在调试构建中启用,并用宏控制
- 不要在重载函数里抛异常或调用 STL 容器——它们本身依赖
new,会导致递归调用
真正难处理的不是单次泄漏,而是周期性小泄漏(比如每帧漏几字节)——它不会立刻崩溃,却会在服务运行数天后突然耗尽内存。这类问题必须靠持续采样+对比基线,而不是等 crash 后再查。











