C++如何实现简单的内存泄漏监控器_C++重载new和delete运算符【高级】

1次阅读

C++如何实现简单的内存泄漏监控器_C++重载new和delete运算符【高级】

为什么重载 operator newoperator delete 是监控内存泄漏的最直接方式

因为所有用 newdelete 分配/释放的内存,都会经过这两个函数(除非显式调用 malloc/free 或禁用全局重载)。不侵入业务代码、不依赖编译器插桩、也不需要链接额外库——只要重载它们,就能在分配时记下地址+大小+调用,在释放时抹除记录。最终程序退出前未被抹除的条目,就是潜在泄漏点。

注意:必须同时重载带 std::size_t 参数的 operator newoperator delete,以及带 noexcept 的版本(c++11 起默认加),否则某些 STL 容器或异常路径下的分配可能绕过你的监控。

如何在重载中安全记录调用和分配信息

仅记录 __FILE____LINE__ 远不够——同一行多次 new 无法区分;而完整调用栈能定位到真正申请位置。推荐用 backtrace()linux/glibc)或 CaptureStackBackTrace()windows)获取帧地址,再用 backtrace_symbols()SymFromAddr() 解析符号。但要注意:

  • backtrace() 在信号处理函数中不安全,不要在 SigsEGV 处理里调用
  • 解析符号需链接 -lbfd -ldl(Linux)或初始化 dbghelp.dllwindows),且调试信息必须存在(-g 编译)
  • 频繁调用栈采集开销大,建议只在 DEBUG 模式启用,或加运行时开关控制

示例关键片段:

立即学习C++免费学习笔记(深入)”;

void* operator new(std::size_t size) {     void* ptr = malloc(size);     if (ptr) {         record_allocation(ptr, size, __FILE__, __LINE__);     }     return ptr; }

为什么必须重载数组版本的 operator new[]operator delete[]

因为 new T[N]delete[] p 调用的是独立的运算符,不是普通 new/delete。漏掉它们会导致数组分配被记录、但释放时不匹配,误报为泄漏。更隐蔽的问题是:如果只重载了单对象版本,而用户写了 new int[10],编译器会调用默认的 operator new[](可能直接转给 malloc),你的监控完全失效。

务必成对实现:

  • void* operator new[](std::size_t size)
  • void operator delete[](void* ptr) noexcept
  • (可选)带 std::align_val_t 的 C++17 对齐版本,否则对 aligned_new 无感知

程序退出时如何可靠触发泄漏报告而不引发二次崩溃

不能依赖全局对象析构顺序(不确定)、也不能靠 atexit() 中做复杂操作(此时堆可能已部分损坏)。稳妥做法是:

  • main() 返回前手动调用 dump_leaks()
  • 或用静态局部变量 + 析构函数,但确保该变量定义在 main 所在编译单元(避免跨 TU 初始化顺序问题)
  • 报告时禁止调用 std::cout(可能已被销毁),改用 write(2, ...)OutputDebugString()
  • 遍历未释放记录时,跳过已 free 但未 delete指针(比如混用 malloc/delete)——这类属于 UB,不归监控器管

真正容易被忽略的是:线程局部存储(TLS)中的分配。若监控器用 thread_local 记录,主线程退出后其他线程仍在运行,数据可能被重复报告或访问已释放内存。统一走全局哈希表 + 读写锁更稳妥。

text=ZqhQzanResources