C++ 内存泄漏:ASAN 工具使用教程

C++ 内存泄漏:ASAN 工具 超详细使用教程

AddressSanitizer(ASAN) 是 C++ 排查内存泄漏的工具,比 Valgrind 快 5~10 倍,GCC/Clang/VS 全都自带,不用额外安装,直接编译就能用。

它能精准检测:

  • 内存泄漏(malloc/new 没 free/delete)
  • 堆溢出、栈溢出
  • 野指针、重复释放
  • 使用已释放内存

一、1 分钟快速上手(直接复制用)

1. 编译命令

GCC / Clang 编译时加一个参数:

bash 复制代码
# 必加:-fsanitize=address
g++ -g -fsanitize=address main.cpp -o test

运行程序:

bash 复制代码
./test

程序退出时,ASAN 会自动打印泄漏位置 + 代码行号。


二、完整示例:检测出泄漏

测试代码(故意写内存泄漏)

cpp 复制代码
#include <iostream>
using namespace std;

void test_leak() {
    // 堆上申请内存,没有 delete → 泄漏
    int* arr = new int[100];
}

int main() {
    test_leak();
    return 0;
}

编译 + 运行

bash 复制代码
g++ -g -fsanitize=address main.cpp -o test
./test

ASAN 直接输出泄漏结果

复制代码
ERROR: LeakSanitizer: detected memory leaks

Direct leak of 400 byte(s) in 1 object(s) allocated from:
    #0 operator new[](unsigned long)  (libasan.so+...)
    #1 test_leak() main.cpp:6
    #2 main main.cpp:10

直接告诉你

  • 哪一行泄漏:main.cpp 第 6 行
  • 泄漏大小:400 字节
  • 哪个函数:test_leak()

三、ASAN 常用编译参数(必记)

1. 基础必用(检测泄漏+越界)

bash 复制代码
g++ -g -fsanitize=address test.cpp -o test
  • -g:保留调试信息(必须加,否则不显示行号)
  • -fsanitize=address:开启 ASAN

2. 只检测内存泄漏(更快)

bash 复制代码
# 只检查泄漏,不检查越界/野指针
g++ -g -fsanitize=leak test.cpp -o test

3. 关闭泄漏检查(只查越界)

bash 复制代码
export ASAN_OPTIONS=detect_leaks=0
./test

四、Windows VS 使用 ASAN

VS 2019 及以上原生支持,不用装插件:

  1. 右键项目 → 属性
  2. C/C++ → 常规 → 启用地址消毒剂 → 选择 是 (/fsanitize=address)
  3. 重新编译运行
  4. 输出窗口直接显示泄漏行号

五、ASAN 泄漏报告怎么看?

报告结构非常清晰:

复制代码
ERROR: LeakSanitizer: detected memory leaks

Direct leak of 400 byte(s) in 1 object(s) allocated from:
    #0 operator new[](unsigned long)
    #1 test_leak() main.cpp:6  <-- 泄漏代码行
    #2 main main.cpp:10

重点看:

  1. Direct leak:直接泄漏
  2. byte(s):泄漏大小
  3. main.cpp:6精确到行号
  4. 调用栈:谁申请了没释放

六、C++ 最常见的泄漏场景(ASAN 一键查出)

1. new 没 delete / new\[\] 没 delete\[\]

cpp 复制代码
int* p = new int;
// 没 delete p; → 泄漏

2. 容器/指针忘记释放

cpp 复制代码
vector<int*> vec;
vec.push_back(new int);
// 程序结束没遍历 delete → 泄漏

3. 类中指针没在析构函数释放

cpp 复制代码
class A {
    int* p;
public:
    A() { p = new int; }
    // ~A() { delete p; } 析构函数忘写 → 泄漏
};

4. 函数内申请内存,没有返回也没有释放

cpp 复制代码
void func() {
    char* buf = new char[1024];
    // 没释放,直接退出 → 泄漏
}

七、注意事项

  1. ASAN 会增加内存占用(约 2x),性能下降约 2 倍
    → 只用于测试/调试不要上线生产环境

  2. 必须加 -g 编译,才能显示行号

  3. 多线程程序也能正常检测。

  4. 有些库(如第三方静态库)没开 ASAN,可能出现误报,可用:

    bash 复制代码
    export ASAN_OPTIONS=detect_leaks=0

总结

  1. C++ 排查内存泄漏首选 ASAN,自带能准确定位到行号。
  2. 编译只需要加:-g -fsanitize=address
  3. 运行程序,退出后自动打印泄漏报告
  4. 能查:泄漏、越界、野指针、重复释放。

你只要把你的代码用这个参数重新编译运行,就能立刻看到所有内存泄漏位置。