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

Windows GDI 对象泄漏排查实战

Windows GDI 对象泄漏排查实战


前言:一次“画布闪烁”引发的 GDI 泄漏排查

几年前维护一个老 MFC 项目时,曾遇到界面频繁“一闪一闪”、卡死的问题。最初怀疑是绘图逻辑,排查多次无果。直到在任务管理器添加了“GDI 对象”列,才发现进程的 GDI 对象数量不断飙升,最终定位到 GDI 对象泄漏(画布绘图相关的 GDI 资源未正确释放)。

现在使用 GDIView 等工具,GDI 泄漏的排查效率已提升数倍。本文结合实战经验,系统介绍 GDI 对象类型、典型泄漏场景、GDIView 工具高效用法及进阶分析技巧。


一、GDI 对象基础与常见泄漏场景

常见 GDI 对象及其生命周期

类型创建API销毁API说明
HDCGetDC, CreateDC, CreateCompatibleDCReleaseDC, DeleteDC设备上下文,画布本体
HBITMAPCreateBitmap, CreateDIBitmap, CreateCompatibleBitmap, LoadBitmapDeleteObject位图对象(像素存储)
HPENCreatePen, CreatePenIndirectDeleteObject画笔,用于线条边框绘制
HBRUSHCreateSolidBrush, CreateHatchBrush, CreateBrushIndirectDeleteObject画刷,用于填充区域色彩
HFONTCreateFont, CreateFontIndirectDeleteObject字体对象
HRGNCreateRectRgn, CreateRoundRectRgn, CreateEllipticRgn, CreatePolygonRgnDeleteObject区域对象,裁剪或绘制区域
HPALETTECreatePaletteDeleteObject调色板(低色深场景)
HMETAFILECreateMetaFile, CopyMetaFileDeleteMetaFile元文件(矢量图记录)
HENHMETAFILECreateEnhMetaFile, CopyEnhMetaFileDeleteEnhMetaFile增强型元文件
HICONCreateIcon, LoadIconDestroyIcon图标对象
HCURSORCreateCursor, LoadCursorDestroyCursor鼠标光标对象
HBITMAP (DIB)CreateDIBSectionDeleteObject设备无关位图

注意: 所有 GDI 对象均需显式销毁,未释放会造成资源泄漏。绝大部分 GDI 对象的销毁函数为 DeleteObject,但 HDC、HICON、HCURSOR、HMETAFILE、HENHMETAFILE 等有专用销毁函数,使用时要注意区分。

典型画布输出泄漏

最易泄漏场景:

  • 在 OnDraw/OnPaint/定时器等高频重绘函数中频繁创建画笔、画刷、字体等对象但未销毁。
  • 使用 new CPen/new CBrush 后未调用 delete 或 DeleteObject。
  • SelectObject 后未恢复原 GDI 对象。

错误示例:

void CMyView::OnDraw(CDC* pDC)
{CPen* pPen = new CPen(PS_SOLID, 2, RGB(255,0,0));pDC->SelectObject(pPen);// ... 绘制 ...// 漏掉 delete pPen,造成泄漏!
}

正确示例:

void CMyView::OnDraw(CDC* pDC)
{CPen pen(PS_SOLID, 2, RGB(255,0,0));CPen* pOldPen = pDC->SelectObject(&pen);// ... 绘制 ...pDC->SelectObject(pOldPen); // 恢复原画笔// pen 析构自动释放,无泄漏
}

二、GDIView 实战——高效定位 GDI 泄漏

1. GDIView 简介与下载

  • GDIView 是一款专门用于监控 GDI 对象分布与趋势的绿色工具。
  • 下载地址:https://www.nirsoft.net/utils/gdi_handles.html

在这里插入图片描述

2. 汉化

GDIView 还提供其他语言版本。为了更改 GDIView,下载相应语言的 zip 文件,解压 ‘gdiview_lng.ini’, 并将其放在您安装了 GDIView 实用程序的同一文件夹中。

在这里插入图片描述

3. 启用自动刷新与增量显示

  • 自动刷新
    在菜单栏选择“刷新”→“自动刷新时更新句柄列表”→选择“每1秒”。这样可以让 GDI 对象数量实时自动更新,方便观察变化趋势。
  • 只显示变化量
    GDIView 会在各 GDI 对象类型后显示与上一次刷新相比的增减值。例如,某一秒 Pen 对象数从100变成110,Diff 会显示 +10,便于直观看到对象激增或回落的时刻。

在这里插入图片描述

4. 实时监控和定位对象类型

  • 在进程列表中选择你的目标进程。
  • 操作程序界面(如反复点击、刷新、拖动窗口等),同时观察画笔、画刷等列的数量和 Diff 增量,判断是否存在持续增加的趋势。
  • 一旦发现某类型对象不断上涨,说明可能有泄漏。

在这里插入图片描述

5. 启用扩展信息窗口

  • 选中目标进程后,点击菜单“查看”→“显示句柄附加信息”。
  • 下方窗口会显示当前所有 GDI 对象句柄、类型、检测编号、首次分配时间等详细信息。

在这里插入图片描述

6. 字段解释与实战用法

  • Detect Counter(检测计数)
    表示此 GDI 对象自被发现以来,每次刷新计数都会 +1。计数越高,说明对象存在的时间越长。某些对象长期不释放、计数飙高,即为泄漏高风险。
  • Detected On(检测到时间)
    记录该对象首次被检测到的具体时间,可结合你实际操作对照分配时机。比如你刚点击某按钮,某些对象的 Detected On 时间就是当前时刻,说明与此操作相关。

在这里插入图片描述

7. 实践建议

  • 利用自动刷新和增量,结合界面操作,快速发现 GDI 对象异常激增的功能区域。
  • 配合扩展信息窗口的 Detect Counter 和 Detected On,重点关注长期未释放的对象,从而回溯到相关操作和源码,逐步锁定泄漏点。

三、GDI 管理与开发实践建议

  • 任何 new/CreateXxx 生成的 GDI 对象,都必须有对应的 DeleteObject/delete。
  • 每次 SelectObject 后保存旧对象并在绘制后恢复。
  • 用 C++ 栈对象(如 MFC 的 CPen/CBrush)代替裸 new。
  • 多线程、定时器、插件等高频绘制场合尤须重视。
  • 发现 GDI 对象异常增长,优先排查高频重绘函数。

GDI 对象与设备上下文(DC)释放铁律

在 Windows GDI 编程中,资源管理有如下通用铁律,务必遵守:

对象来源释放方法说明
CreateXxx 产生的 GDI 对象(如 Pen、Brush、Font、Bitmap、Region 等)DeleteObject绝大多数 GDI 对象均用DeleteObject销毁
CreateDC、CreateCompatibleDC 产生的 HDCDeleteDC自己创建的内存/兼容 DC,必须 DeleteDC
GetDC、GetWindowDC 等获得的 HDCReleaseDC获取系统 DC 后必须 ReleaseDC 归还系统

切记:不能混用 DeleteDC 和 ReleaseDC,也不能用 DeleteObject 释放 DC!

正确示例代码
// 1. Create出来的GDI对象用DeleteObject
HPEN hPen = CreatePen(...);
DeleteObject(hPen);// 2. Create出来的DC用DeleteDC
HDC hMemDC = CreateCompatibleDC(hWndDC);
DeleteDC(hMemDC);// 3. GetDC得出的DC用ReleaseDC
HDC hWndDC = GetDC(hWnd);
// ... 绘图 ...
ReleaseDC(hWnd, hWndDC);
错误示例(不要这样做)
// 不要用ReleaseDC释放Create出来的DC
HDC hMemDC = CreateCompatibleDC(hWndDC);
ReleaseDC(hWnd, hMemDC); // 错误!// 不要用DeleteDC释放GetDC获得的DC
HDC hWndDC = GetDC(hWnd);
DeleteDC(hWndDC); // 错误!

遵守以上资源释放规则,是避免GDI泄漏和系统稳定的基础!


四、进阶:更强大分析工具推荐

  • VMMap:内存分布分析,统计 GDI/USER 对象。
  • Process Explorer:实时查看进程 GDI/USER 对象、资源泄漏趋势。
  • WinDbg: 配合符号表和 !gdi 命令,可查看 GDI 对象分配堆栈。

五、结语与建议

  • GDI 对象泄漏极易导致界面异常、崩溃等顽疾,桌面开发者务必警惕。
  • 善用 GDIView 等工具,能高效定位泄漏类型和增减趋势。
  • 平时多做专项检查,保持良好资源管理习惯。
  • 复杂场景可用 VMMap、Process Explorer、WinDbg 做深入诊断。
  • Visual Studio 2022 的内存快照/诊断工具可以精准地定位到 C++ GDI 对象泄漏的具体位置和类型。
  • GDIView 等工具则更适合系统级体检、无源码排查或 Release 环境监控补充使用。

如需更多画布绘制与 GDI 资源管理代码示例,或有 GDI 相关疑难,欢迎留言交流!

实用工具一览:

  • GDIView 官网
  • VMMap 官网
  • Process Explorer 官网
  • WinDbg 官方文档

相关文章:

  • Vue 生命周期全解析:从创建到销毁的完整旅程
  • [网页五子棋][匹配模块]实现胜负判定,处理玩家掉线
  • 测试面试题 手机号验证码登录测试用例
  • 论文导读 | 动态图存储与事务处理系统总结
  • 敏捷开发中如何避免过度加班
  • 代码随想录 算法训练 Day22:回溯算法part01
  • AIGC 基础篇 高等数学篇 03 中值定理与导数应用
  • 大数据学习(130)-zookeeper
  • Linux系统-基本指令(6)
  • 幂等性:保障系统稳定的关键设计
  • C++内联函数(inline)的作用
  • BUU MISC(持续更新)
  • Linux容器篇、第一章docker命令总结表
  • NLP学习路线图(二十二): 循环神经网络(RNN)
  • 【Python指南】离线安装顽固复杂的第三方库指南
  • 嵌入式系统中常用的开源协议
  • (1-6-3)Java 多线程
  • 深度解析ArrayList
  • Java DLL依赖缺失解决思路和修复过程(Windows版本)
  • django paramiko 跳转登录
  • 网站图片分辨率尺寸/软文是什么
  • 西安做网站哪家好/营销广告文案
  • 网站支持qq登录怎么做/灰色行业关键词优化
  • 独立网站怎样建设/成都抖音seo
  • 温州市城乡建设建档案馆网站/晋江友情链接是什么意思
  • 做网站如何语音对话/怎样做好网络推广呀