内存泄漏是动态分配内存后未释放,导致运行时内存持续增长并可能被OOM杀死;C++中需严格配对new/delete、malloc/free,避免错配或遗漏;推荐用RAII智能指针(unique_ptr/shared_ptr/weak_ptr)和工具(valgrind、ASan)检测与预防。
内存泄漏就是 new 了没 delete,或 malloc 了没 free
它不是程序崩溃那一刻才出问题,而是像漏水的水管——每次运行都悄悄多占一点内存,跑得越久越卡,最终可能被系统 OOM 杀掉。C++ 没垃圾回收,
、
、
这些调用后,必须有且仅有一次对应的
、
或
;漏掉、重复、错配(比如
配
),都算泄漏。
常见错误现象:
报
或
程序长时间运行后 RSS 内存持续上涨,
或
里看
列不停变大
用
却在循环引用里忘了
断环
用 valgrind 快速定位泄漏点(Linux/macOS)
valgrind 是目前最靠谱的动态检测工具,不用改代码,但只支持 x86/x64,macOS 12+ 需用
(注意:Apple Silicon 上原生不支持,得用 Rosetta 启动)。
实操建议:
立即学习
“
C++免费学习笔记(深入)
”;
编译时加
,否则 valgrind 只能显示汇编地址,看不到函数名和行号
避免优化等级过高(比如
),
最稳妥
关注输出里
这类带源码位置的行,优先修这些
如果看到
,多数是全局缓存或日志缓冲区,不一定算 bug,但得人工确认是否预期
RAII 是防泄漏的底层逻辑,不是语法糖
手动配对
/
在复杂控制流(比如中间有
、异常抛出)里极易出错。RAII 的核心是“资源即对象”:把内存绑定到栈对象生命周期上,靠析构函数自动释放。
C知道
CSDN推出的一款AI技术问答工具
下载
该用什么,怎么选:
单个对象 → 用
:
,离开作用域自动
需要共享所有权 → 用
,但警惕循环引用:
持有对方,
观察对方
数组 → 必须用
或
,
配
会只调
不调
,UB
别再裸写
—— 除非你在写底层容器或性能敏感的内联热路径,且能 100% 控制所有出口
ASan(AddressSanitizer)比 valgrind 更快,但只能捕获部分泄漏
ASan 是编译器内置检测器(GCC/Clang 支持),启动快、开销小,但它默认只报内存错误(越界、UAF),泄漏检测需额外开启:
,且只对
/new 系列有效,不覆盖
或自定义分配器。
使用场景和限制:
开发机日常编译加
,跑单元测试能快速发现新引入的泄漏
ASan 报的
会给出调用栈,但不如 valgrind 全面(比如不报
)
Windows 下用
+
替代,但体验和精度打折扣
CI 流水线里建议 valgrind 和 ASan 都跑,互补盲区
真正难的不是找到泄漏,而是判断“这块内存该不该释放”——比如一个全局
缓存,生命周期贯穿整个进程,valgrind 会报它 leaked,但其实是设计如此。这时候得翻文档、问作者、看初始化逻辑,而不是无脑加
。
newnew[]mallocdeletedelete[]freenewfreevalgrinddefinitely lostpossibly losttophtopRESstd::shared_ptrstd::weak_ptrvalgrind --tool=memcheck --leak-check=full --show-leak-kinds=all ./your_program-g-O2-O0 -gat 0x...: your_function (file.cpp:42)still reachablenewdeletereturnstd::unique_ptrauto p = std::make_unique(42); deletestd::shared_ptrstd::shared_ptrstd::weak_ptrstd::unique_ptrstd::vectorstd::unique_ptrnew int[10]deletedelete[]new-fsanitize=address,leakmallocmmap-fsanitize=address,leak -gLeakSanitizer: detected memory leaksstill reachableApplication VerifierDr. Memorystd::mapdelete