Appearance
内存泄漏:危害 / 检测 / 定位 / 避免
一、什么是内存泄漏
程序申请了内存但无法再释放(丢失指向它的指针,或引用计数永不归零),随运行不断增加占用直至 OOM/崩溃。
常见成因:
new/malloc后忘记delete/free(尤其异常路径提前 return)。- 循环引用循环持有 shared_ptr(见 smart-pointers)。
- 容器长期持有无用数据、缓存无上限。
- 全局/静态对象持有大资源。
- 第三方库不释放、new 与 delete 不匹配(new[]却 delete)。
二、检测工具(面试常问用什么查)
| 工具 | 方式 | 场景 |
|---|---|---|
| Valgrind Memcheck | 动态二进制插桩,拦截 malloc/free 并跟踪未释放块;还能查越界/use-after-free | 开发/单测,最经典 |
| AddressSanitizer (ASan) | 编译期插桩(-fsanitize=address),更快更准,查堆越界/UAF/泄漏(LSan) | CI/本地,比 valgrind 快得多 |
| valgrind massif / heaptrack | 堆 profiler,看峰值与分配热点 | 定位"为什么涨" |
| mtrace(glibc) | 记录 malloc/free 配对 | 轻量脚本式 |
| 自定义 new/delete 计数替换 | 覆盖 operator new/delete 统计未配对 | 有符号时最直观 |
三、定位思路(结合 core / 工具)
泄漏发生在「没有及时释放」的代码路径。定位步骤:
- 用 LSan/ valgrind 得到泄漏的分配调用栈(哪个函数 new 出来没 free)。
- 看该对象生命周期:决定在何时 delete / 用 unique_ptr / shared。
- 若是长生命周期容器缓存 → 换成按需清理/带容量上限结构。
- 若是循环引用 → 用 weak_ptr 破环。
- 用 heaptrack 看内存随时间增长的分配点,锁定增长来源。
四、如何"避免"(预防为主)
- RAII + 智能指针(unique_ptr 默认,shared 仅在确实共享)兜住常规路径。
- 对象作用域清晰,用栈对象优先于堆。
- 容器该
clear/shrink及时清理;缓存设上限。 - new/delete、new[]/delete[] 严格配对(或用 vector/string/unique_ptr<T[]> 替代裸数组)。
- 早期接入 ASan + 泄漏检测到 CI。
- 资源(文件锁 fd、线程、socket)同样按 RAII 释放,不只盯内存。
五、相关崩溃排查速记
| 现象 | 常见原因 | 工具 |
|---|---|---|
| 崩溃/core dump | 段错误:空指针、越界、UAF | core dump + gdb bt |
| 内存持续增长 | 泄漏 | valgrind/LSan/heaptrack |
| 程序被 OOM killer | 泄漏或过度分配 | dmesg、/proc |
| 偶发崩溃/难复现 | UAF、数据竞争 | ASan + TSan |
如果程序崩溃(core dump)怎么定位:开启 ulimit -c unlimited → 崩溃生成 core → gdb 程序 core -ex 'bt' / bt full 看函数调用栈 → 定位到崩溃点(空指针解引用/向量越界等),这也是面试「linux 程序崩溃怎么定位」的标准答法。