Skip to content

内存泄漏:危害 / 检测 / 定位 / 避免

一、什么是内存泄漏

程序申请了内存但无法再释放(丢失指向它的指针,或引用计数永不归零),随运行不断增加占用直至 OOM/崩溃。

常见成因:

  1. new/malloc 后忘记 delete/free(尤其异常路径提前 return)。
  2. 循环引用循环持有 shared_ptr(见 smart-pointers)。
  3. 容器长期持有无用数据、缓存无上限。
  4. 全局/静态对象持有大资源。
  5. 第三方库不释放、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 / 工具)

泄漏发生在「没有及时释放」的代码路径。定位步骤:

  1. LSan/ valgrind 得到泄漏的分配调用栈(哪个函数 new 出来没 free)。
  2. 看该对象生命周期:决定在何时 delete / 用 unique_ptr / shared。
  3. 若是长生命周期容器缓存 → 换成按需清理/带容量上限结构。
  4. 若是循环引用 → 用 weak_ptr 破环。
  5. heaptrack 看内存随时间增长的分配点,锁定增长来源。

四、如何"避免"(预防为主)

  • RAII + 智能指针(unique_ptr 默认,shared 仅在确实共享)兜住常规路径。
  • 对象作用域清晰,用栈对象优先于堆。
  • 容器该 clear/shrink 及时清理;缓存设上限。
  • new/delete、new[]/delete[] 严格配对(或用 vector/string/unique_ptr<T[]> 替代裸数组)。
  • 早期接入 ASan + 泄漏检测到 CI。
  • 资源(文件锁 fd、线程、socket)同样按 RAII 释放,不只盯内存。

五、相关崩溃排查速记

现象常见原因工具
崩溃/core dump段错误:空指针、越界、UAFcore 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 程序崩溃怎么定位」的标准答法。

C++ 面试八股 · VitePress 版