C++ / a working model

43 / 80   ·   C++14   ·   约 10 分钟

内存泄漏 Memory leaks:从责任到证据

先记住这句话

泄漏首先是资源没有在约定时间被正确回收,不是简单观察进程内存有没有下降。用 RAII 覆盖异常路径,再结合 LeakSanitizer 的分配栈与重复工作负载区分失联分配、引用环、无界缓存以及分配器保留。

本篇内容
  1. 先定位生命周期契约
  2. 用动态工具得到分配证据
  3. 用可观察状态确认回收
  4. 运行示例
  5. 动手练习

先定位生命周期契约

典型泄漏是动态分配后丢失唯一的释放路径,例如裸指针被覆盖或异常越过手工 delete。还有一种业务泄漏:对象仍可从全局容器访问,但缓存无限增长、过期任务永不移除。检测器对可达对象的判断不等于业务已经接受其永久存活。

shared_ptr 也可能通过环形成泄漏,因此不能只搜索裸 new。审查时沿拥有关系寻找谁应释放谁,检查回调捕获、订阅注册和全局缓存。真正的修复是明确责任与退出条件,而不是在程序结束前遍历所有地址盲目 delete。

用动态工具得到分配证据

在支持的平台上,可用 clang++ -std=c++14 -g -O1 -fsanitize=address -fno-omit-frame-pointer example.cpp -o example 编译链接,再运行可重复工作负载。ASan 在部分平台集成 LSan,必要时通过 ASAN_OPTIONS=detect_leaks=1 开启;也可单独使用 -fsanitize=leak

阅读报告时保留首次分配调用栈,缩小到创建、转移和退出路径。动态检测只覆盖实际运行到的路径,无报告不是无泄漏证明。异常、取消和早退往往比成功路径更容易暴露缺失清理,必须纳入复现,而不是仅运行一次正常结束。

用可观察状态确认回收

示例在失败路径与成功路径分别创建 Worker,靠 RAII 销毁,再断言存活计数回到零。它不故意泄漏,不把错误运行当教学前提。计数能说明这种对象的构造析构平衡,但不是全进程泄漏检测,仍应与工具报告结合。

内存监控还受分配器缓存、容量保留和碎片影响。vector 清空后 size 归零但 capacity 可以保留;操作系统看到的驻留量也可能滞后。先证明对象是否仍被拥有,再分析底层存储为什么保留,避免为“降低曲线”引入反复分配和更差性能。

容易答错的地方

  • 用进程退出后操作系统回收地址空间作为长期服务的清理策略,会掩盖运行期间无界增长。
  • 忽略或屏蔽所有检测报告不能证明修复;应保存复现输入,并对同一路径确认报告消失。

运行一个例子

最低标准 C++14 · 完整程序 · 下载 .cpp

#include <cassert>
#include <memory>
#include <stdexcept>

struct Worker {
    static int alive;
    Worker() { ++alive; }
    ~Worker() { --alive; }
};
int Worker::alive = 0;

void run(bool fail) {
    auto worker = std::make_unique<Worker>();
    if (fail) throw std::runtime_error("cancelled");
}

int main() {
    bool caught = false;
    try { run(true); }
    catch (const std::runtime_error&) { caught = true; }
    assert(caught && Worker::alive == 0);
    run(false);
    assert(Worker::alive == 0);
}

在本地编译

g++ -std=c++14 -Wall -Wextra -Wpedantic -pthread memory-leaks.cpp -o example && ./example

预期结果

预期:正常退出、无输出;所有 assert 通过。

CHECK YOUR UNDERSTANDING

合上答案,试着解释。

服务每处理一批任务后 RSS 上升,但所有 Worker 都已析构。能直接判定没有泄漏吗?

查看参考答案

不能。Worker 计数只覆盖一种对象,其他分配、缓存或引用环仍可能增长。固定工作负载重复运行,结合 LSan 或堆分析的分配栈,检查全局容器尺寸和保留容量。若业务拥有量稳定而 RSS 在预热后稳定,才有证据进一步调查分配器缓存;不能仅凭析构计数或一次内存下降下结论。

继续查证

标准草案链接会随工作草案更新;本文版本标记对应示例最低要求,不表示草案中的所有新规则都适用于旧标准。

回到目录