当前位置: 首页 > ai >正文

使用 Deleaker 精准定位内存与 GDI 资源泄漏

使用 Deleaker 精准定位内存与 GDI 资源泄漏

在日常 C++ / MFC 桌面应用开发中,内存泄漏与 GDI 资源泄漏常常会导致程序崩溃、界面闪烁、资源耗尽等问题。本文将介绍如何安装并使用 Deleaker 工具,并与 Visual Studio 自带的内存快照工具进行对比,帮助开发者选择最合适的泄漏检测手段。


一、Deleaker 简介

Deleaker 是一款功能强大的内存和资源泄漏检测工具,支持:

  • 堆内存(new / malloc)泄漏检测
  • GDI 对象泄漏(CreatePen / CreateFont 等)
  • USER 对象泄漏(如窗口、菜单、图标)
  • 句柄(CreateEvent / CreateFile 等)泄漏
  • DC(设备上下文)泄漏
  • 与 Visual Studio 无缝集成,支持快照比对、堆栈回溯、符号定位

二、安装与首次配置

1. 下载与安装

前往官网:https://deleaker.com,下载安装包。

安装过程中建议勾选:

  • ✅ Visual Studio 插件(建议勾选)
  • ✅ 独立 GUI 工具(可用于调试任意 EXE)

2. 第一次启动时的提示说明

① 选择分析模式(Profiler Mode)

启动 Deleaker 后会提示:

在这里插入图片描述

你需要根据项目类型选择合适的分析模式:

  • Unmanaged Code Profiling Mode(推荐 C++ 开发者选择)

    • 用于:原生 C/C++ 项目、MFC 应用程序、Win32 API 程序
    • 检测内容:堆内存泄漏、GDI 对象、USER 对象、系统句柄等
    • 特点:无需 CLR 支持,适合桌面软件、驱动工具、传统 C++ 项目
  • .NET Profiling Mode(适用于 C#/.NET 程序)

    • 用于:C#、VB.NET、WPF、WinForms 等托管应用程序
    • 检测内容:.NET 托管对象、未释放的 System.IO.Stream、数据库连接等
    • 特点:需启用 CLR Profiler 接口,可追踪托管堆

如果你不确定,可以参考以下简表:

项目类型推荐模式
C++ MFC 桌面应用Unmanaged
C++ 控制台程序Unmanaged
C# WinForms/WPF 应用.NET
.NET Core 控制台应用.NET
混合 C++/CLI 工程建议拆分模块独立调试

⚠️ 注意:选择后仍可在 Deleaker 设置中修改,不是永久绑定。

② 是否启用调试监控提示

> **Would you like to allow Deleaker to monitor allocations and deallocations of objects to collect leaks?**

  • 选择 Yes:允许 Deleaker 注入检测器,记录资源分配情况(建议)
  • 勾选“Don’t ask me again”,避免每次启动弹出
③ 选择监控的资源类型

> **Select allocation types that Deleaker will monitor**

默认全选:

  • ✅ memory(new/malloc)
  • ✅ GDI objects(画笔、字体等)

你也可以只保留感兴趣的部分(例如仅 GDI)提高运行速度。


三、基本用法

1. 拍摄快照(Snapshot)

  • 启动调试(F5)
  • 打开 Deleaker 面板,点击 Take Snapshot
  • 多次快照可对比内存变化(使用 Compare with...

在这里插入图片描述
在这里插入图片描述

2. 定位泄漏

  • 快照后资源列表中会显示未释放对象
  • 支持按照类型(Heap / GDI / Handle)筛选
  • 双击条目可跳转至源代码位置

四、常见资源泄漏场景示例

1. 堆内存泄漏(new/malloc

void TestLeak()
{for (int i = 0; i < 10; ++i){int* p = new int[100];// 未 delete[] p}
}

问题:分配了堆内存,但未释放,导致堆空间不断增长。Deleaker 可准确捕捉每个分配点。

2. GDI 资源泄漏(CreateXxx

void CMyDialog::OnPaint()
{for (int i = 0; i < 5; ++i){HBRUSH hBrush = CreateSolidBrush(RGB(0, 255, 0));HDC hdc = GetDC()->GetSafeHdc();SelectObject(hdc, hBrush);// 忘记 DeleteObject(hBrush);}
}

问题:GDI 对象是系统资源,如果不释放,会造成绘图失败、UI 闪烁等问题。

在这里插入图片描述

❗ Visual Studio 快照工具的表现:

使用 Visual Studio 的“诊断工具 > 内存使用”进行快照时,对上述 GDI 资源分配并不会显示明确的分配位置。

在快照报告中看到类似:

在这里插入图片描述

这表示该对象不是通过 CRT 分配,VS 工具无法关联它与代码行。

结论:CreateSolidBrush 创建的资源不会被 VS2022 自带工具标记为明确泄漏源,也不会显示调用堆栈或源代码位置。

3. GDI 泄漏(new CPen

for (int i = 0; i < 10; ++i)
{CPen* pPen = new CPen(PS_SOLID, 2, RGB(0, 128, 255));// 忘记 delete pPen → CPen 析构不会调用 DeleteObject
}

说明:MFC 封装的 GDI 对象(如 CPen、CBrush)如果使用 new 分配,也必须手动 delete 才能释放其关联 GDI 资源。

在这里插入图片描述

① Deleaker 检测到泄漏
  • 文件:CRobotTaskDlg.cpp, line 159
  • 类型:Heap memory(堆内存)
  • 条目数量:26 个泄漏

说明这段代码:

CPen* pPen = new CPen(PS_SOLID, 2, RGB(0, 128, 255));

确实在循环里不断分配 CPen 对象,但从未 delete堆对象未析构,GDI 对象也未 DeleteObject,构成双重泄漏。

② 输出窗口 dump_complete + atlTraceGeneral

MFC 的内存调试(如 _CrtDumpMemoryLeaks() 或使用 afxMemDF):

  • 输出:

    detected memory leaks!
    
  • 并列出了每个泄漏块的地址和大小,例如:

    {7456} normal block at 0x000001236F602820, 16 bytes long
    

这与 Deleaker 的结果形成 双重交叉验证,表示泄漏 确实存在,且可以定位具体分配行数

③ 总结这次验证意义
工具检测内容优点
Deleaker堆泄漏 + GDI 泄漏,精确到源文件支持快照对比、GDI 专项、定位调用栈
VS 输出窗口 (_CrtDumpMemoryLeaks)堆泄漏自带,轻量,适合 Debug 阶段

五、Deleaker 漏洞验证与修复成功案例

在开发自定义控件 CBlButton 时,使用了多个 GDI 对象(CreatePenCreateSolidBrushGetStockObject 等),初期由于部分 DeleteObject 遗漏或误释放,导致 Deleaker 检测到如下泄漏:

泄漏位置:BltButton.cpp, line 272
类型:GDI 对象(笔、画刷)

❌ 修复前问题:

  • CreatePen 创建的对象未释放或错误释放(重复释放旧对象)
  • 字体(HFONT)使用 SelectObject 设置后未还原旧字体
  • 菜单小三角中误将已释放的 hBrush / hPen 重复释放(应释放局部 hbrDrop / hPenDrop

例如以下代码存在典型问题:

// 错误释放菜单绘图中局部资源
SelectObject(hDC, holdBrush);
SelectObject(hDC, holdPen);
DeleteObject(hBrush);   // ❌ 已在上一段释放,应为 hbrDrop
DeleteObject(hPen);     // ❌ 应为 hPenDrop

在这里插入图片描述

✅ 修复操作:

  • 所有 GDI 对象遵循 Create → Select → Restore → Delete 模式
  • 字体部分使用 hOldFont = SelectObject(...) 并还原
  • 修复了 DrawTriangle 逻辑,确保仅释放 Create 出来的局部资源

修复后的关键片段:

// 菜单项小三角绘制
HPEN hPenDrop = CreatePen(...);
HBRUSH hbrDrop = CreateSolidBrush(...);
HBRUSH hOldBrush = (HBRUSH)SelectObject(hDC, hbrDrop);
HPEN hOldPen = (HPEN)SelectObject(hDC, hPenDrop);
// Polygon(...)
SelectObject(hDC, hOldBrush);
SelectObject(hDC, hOldPen);
DeleteObject(hbrDrop);      // ✅ 正确释放
DeleteObject(hPenDrop);     // ✅ 正确释放

字体部分:

HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
HFONT hOldFont = (HFONT)::SelectObject(hDC, hFont);
// ... 文字绘制
::SelectObject(hDC, hOldFont); // ✅ 恢复原字体

📸 快照对比结果:

最终快照显示:

No leaks found

并提示:

0 of 16 shown(无泄漏对象)

说明所有 DrawItem() 中涉及的资源都已成功释放,Deleaker 报告“干净”状态。

在这里插入图片描述

🔍 对比前后改动关键点:

对象类型修复前修复后
HPEN未 delete 或重复释放delete 与原始对象匹配
HBRUSH部分遗漏或重复释放局部变量专属释放
HFONT未还原还原旧字体对象

六、Deleaker 与 Visual Studio 自带内存分析对比

功能维度Visual Studio 自带内存快照Deleaker
是否支持堆泄漏检测(new/malloc)
是否支持 GDI 对象泄漏检测
是否支持 USER/句柄泄漏检测
是否可对比快照差异✅(有限)✅(支持任意对比)
是否显示调用堆栈✅(摘要)✅(完整堆栈)
是否跳转源代码
是否检测程序退出前泄漏
是否支持 GUI 独立使用

✅ 结论

  • Visual Studio 快照工具:适合简单调试 new 内存泄漏,对非托管资源无能为力。
  • Deleaker:适合全面检测桌面应用程序,特别适用于 UI/GDI 密集型项目。

七、推荐实践

  1. 每次版本发布前使用 Deleaker 检查内存与 GDI 泄漏。
  2. 对于 new 分配,建议使用智能指针(如 std::unique_ptr)管理。
  3. 所有 CreateXxx(如 CreateFont, CreatePen)资源,务必对应调用 DeleteObject
  4. 拍摄多个快照(初始化 → 操作后 → 退出前)进行泄漏增长对比分析。
  5. 使用 Compare with... 找出精确的资源增长点。

八、结语

对于 C++/MFC/Win32 桌面开发者,Deleaker 是一款值得长期集成的调试利器,特别是在 UI、绘图、资源频繁分配的程序中,它能够清晰、精准、实用地帮助我们及时发现并修复潜在问题。

借助 Deleaker,我们不仅可以识别 new 导致的堆泄漏,更重要的是能发现诸如 CreateSolidBrushCreateFont系统 GDI 资源未释放的问题,这是 Visual Studio 自带工具无法实现的。

http://www.xdnf.cn/news/12187.html

相关文章:

  • Matplotlib 库来可视化频谱泄漏和加窗的效果
  • 【如何做好应用架构?】
  • RTOS:创建队列(含源码分析)
  • 搭建DNS域名解析服务器(正向解析资源文件)
  • 数据结构:递归:泰勒展开式(Taylor Series Expansion)
  • 如何搭建自动化测试框架?
  • simulink有无现成模块可以实现将三个分开的输入合并为一个[1*3]的行向量输出?
  • nginx 服务启动失败问题记录
  • 华新精科IPO“上会” 四大疑惑待解
  • LeetCode | 滑动窗口的原理及真题解析
  • JavaScript 数组与流程控制:从基础操作到实战应用
  • 八皇后问题深度解析
  • 05【Linux经典命令】Linux 用户管理全面指南:从基础到高级操作
  • 深入浅出工厂模式:从入门到精通(2025最新版)
  • Linux多线程
  • java教程笔记(九)-异常处理,枚举类,反射机制,注解
  • 使用 Preetham 天空模型与硬边太阳圆盘实现真实感天空渲染
  • 益莱储参加 Keysight World 2025,助力科技加速创新
  • Python正则表达式re模块
  • 资产智慧管理安全监测中心
  • 【物联网-TCP/IP】
  • 【AI学习】KV-cache和page attention
  • 【机器学习】主成分分析 (PCA)
  • AIGC图像去噪:核心原理、算法实现与深度学习模型详解
  • C++课设:智能优惠快餐点餐系统
  • 新建网站部署流程
  • glibc 交叉编译
  • Ansys Maxwell:线圈和磁体的静磁 3D 分析
  • 深度学习之模型压缩三驾马车:基于ResNet18的模型剪枝实战(1)
  • USB-C/HDMI 2.0 2:1 SW,支持4K60HZ