C++ 内存泄漏排查:从裸 new 到 RAII 修复,附 ASan 实战

C++ 内存泄漏排查:从裸 new 到 RAII 修复,附 ASan 实战

背景

机器人感知节点里,每帧点云、IMU 数据都要分配缓冲。

如果用了裸 new 但忘了 delete,跑几小时内存就持续上涨。

最终节点被 OOM 杀掉,线上排查只能看 RSS 曲线。

本文用一个最小例子,演示泄漏怎么产生、怎么用 ASan 定位、怎么用现代 C++ 修掉。

反面教材:最经典的泄漏

cpp 复制代码
#include <iostream>
#include <string>

void ProcessPointCloud(const std::string& sensor_id) {
    // 预处理缓冲:运行时才知道大小,必须 new
    int* buffer = new int[1024];

    // 模拟处理
    buffer[0] = 42;
    std::cout << "  [" << sensor_id << "] 处理完成" << std::endl;

    // 忘记 delete[] buffer;  <-- 泄漏点
}

int main() {
    std::cout << "=== 触发内存泄漏 ===" << std::endl;
    for (int i = 0; i < 3; ++i) {
        ProcessPointCloud("Lidar_" + std::to_string(i));
    }
    std::cout << "程序结束,ASan 会报告泄漏详情。" << std::endl;
    return 0;
}

编译运行:

bash 复制代码
g++ -std=c++17 -fsanitize=address -g exercise2_leak.cpp -o exercise2_leak && ./exercise2_leak

ASan 会直接打印泄漏报告,定位到 new int[1024] 那一行。

修复版:两种现代写法

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <memory>

// 修复方式一:用 vector 管理动态数组
void ProcessPointCloudVector(const std::string& sensor_id) {
    std::vector<int> buffer(1024);
    buffer[0] = 42;
    std::cout << "  [" << sensor_id << "] vector 版处理完成" << std::endl;
}

// 修复方式二:用 unique_ptr 管理动态数组
void ProcessPointCloudUnique(const std::string& sensor_id) {
    auto buffer = std::make_unique<int[]>(1024);
    buffer[0] = 42;
    std::cout << "  [" << sensor_id << "] unique_ptr 版处理完成" << std::endl;
}

int main() {
    std::cout << "=== 修复后:无泄漏 ===" << std::endl;
    for (int i = 0; i < 3; ++i) {
        ProcessPointCloudVector("Lidar_" + std::to_string(i));
    }
    for (int i = 0; i < 3; ++i) {
        ProcessPointCloudUnique("Lidar_" + std::to_string(i));
    }
    std::cout << "程序结束,ASan 报告干净。" << std::endl;
    return 0;
}

编译运行:

bash 复制代码
g++ -std=c++17 -fsanitize=address -g exercise2_fixed.cpp -o exercise2_fixed && ./exercise2_fixed

跑完 ASan 报告是干净的,零泄漏。

现代 C++ 内存管理规则

场景 写法
普通对象 std::make_unique<T>() / std::make_shared<T>()
动态数组 std::vector<T>
独占资源 std::unique_ptr<T>
共享资源 std::shared_ptr<T>
观察资源 std::weak_ptr<T>
对象池 / 对齐内存 内部用 placement new,外层 RAII
与 C 接口交互 裸指针进,RAII 包装出

结论

业务层不写裸 new/delete。

底层或性能敏感场景会用,但必须被 RAII 包起来。

排查流程:复现 → 加 -fsanitize=address 重编 → 跑一遍看报告 → 修掉 → 再跑一遍确认归零。

相关推荐
十年Java程序媛1 小时前
Java 面试常问 this、super:构造方法调用顺序避坑
java·开发语言·spring boot·面试·个人开发
yuniko-n2 小时前
【JUC】内存屏障
java·开发语言
Linux运维技术栈2 小时前
近期 PHP 高危安全风险排查与多环境升级实操
开发语言·安全·php
凯瑟琳.奥古斯特2 小时前
力扣1015C++解法技巧
c++·算法·leetcode·链表·职场和发展
Fnetlink12 小时前
Fnet 云网安 261023
运维·开发语言·网络·人工智能·安全·web安全·php
悠仁さん2 小时前
【算法C++】双指针(一)
数据结构·c++·算法
ESDWAN2 小时前
SD-WAN 主备切换为什么仍会断网?从检测、切换到会话恢复分析
运维·开发语言·网络·php
jimy12 小时前
lvalue、xvalue、prvalue(二):引用是哪一类?
开发语言·c++·1024程序员节
cxyxixiy2 小时前
数智码力:数据核对不用人工比对,Python 快速完成多表格差异校验
开发语言·python·学习