如何调试 GDI 对象泄漏?

Posted

技术标签:

【中文标题】如何调试 GDI 对象泄漏?【英文标题】:How to debug GDI Object Leaks? 【发布时间】:2012-01-08 07:01:12 【问题描述】:

我不太确定该怎么做。这是一个大型应用程序,我们的大多数表单上都有 GDI 对象“泄漏”。

有帮助的工具吗?有没有关于如何使用这种工具的教程?

我是否应该开始从我们的表单中删除代码,直到我缩小违规者的范围? (有很多代码)。

【问题讨论】:

你怎么知道你有 GDI 对象泄漏? @JimMischel 如果我们多次打开和关闭某些表单,我们的应用程序将崩溃(我们最常用的表单之一在整个应用程序崩溃之前打开/关闭约 75 次)。我忘记了确切的错误是什么,但它与 Windows 中的 1,000 个句柄限制有关(1,000 是注册表设置,最多可以更改为 ~32,000)。我已经断断续续地研究了几个月(我似乎无法弄清楚我们做错了什么,因此我无法弄清楚如何解决它)。 @XenoPuTtSs 听起来更像是您需要使用 adplus 进行内存转储并使用带有 SOS 的 winDbg 来查看根本原因。确切的错误显然会很有帮助。 所以你不一定会泄漏 GDI 对象,而是句柄,可能是 GDI 对象、用户控件等。在我看来,你的程序中的某些东西正在维护对表单的引用,或者当您关闭表单时,可能是表单上的某些控件。这将阻止表单(或引用的控件)被正确处理。您是否在某处有一个持久性集合,其中包含对表单或控件的引用? 【参考方案1】:

仅 GDI 对象超过 10,000 个对象限制是非常罕见的,当您不自己调用它们的 Dispose() 方法时,垃圾收集器会处理它们。一个更有可能的故障模式是超出窗口的对象限制。这在 Winforms 中很容易做到,当您没有显式处理已删除的控件时,Controls.Clear() 或 Controls.Remove() 会让您快速到达那里。垃圾收集器无法清理它们。

您可以从 Taskmgr.exe 的“进程”选项卡中获得良好的诊断。查看 + 选择列并勾选句柄、用户对象和 GDI 对象。使用过程时请注意这些数字。其中一个的数量稳步上升是一个明确的信号,当它拒绝给你更多时,你会让 Windows 轰炸你的程序。每个配额的默认配额为 10000。 USER Objects 表示您在 Controls.Clear/Remove 方面存在问题,GDI Objects 表示您正在泄漏 System.Drawing 对象。 Perfmon.exe 是一个很好的工具,可以查看垃圾收集器是否经常运行以释放未释放的 System.Drawing 对象。

根据常识,在需要时显式调用 Dispose() 是一种很好的做法。特别是对于 Image 和 Bitmap 对象,它们占用很少的 GC 内存,但占用大量非托管内存,当您不处置它们时,很容易用 OOM 轰炸程序。当心 Properties.Resources 中令人讨厌的陷阱,每次使用它都会得到一个新对象,并且需要处理它。始终在绘制代码中使用using 语句。

【讨论】:

>>Controls.Clear() 或 Controls.Remove() 会在您不明确处置已移除的控件时快速到达那里。垃圾收集器无法清理它们。 删除的控件通过将它们放置在一个称为“停车窗口”的隐藏窗口中来保持活动状态。准备好重新设置到另一个窗口。非常好的功能,因为它保持原生窗口完好无损。但如果没有发生这种移动,或者它们没有被处理掉,它们就会永远泄漏。 我正在使用 taskmgr.exe,我看到我的 GDI 对象越来越高。最终它达到一个限制(10,000)并且应用程序崩溃(顺便说一句,10,000 可通过注册表“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\GDIProcessHandleQuota”进行配置)是术语“停车窗口”官方,如果不是,这个功能叫什么? 忘掉停车窗口吧,这不是你的问题的原因。注册表黑客是无用的,它只是推迟了不可避免的事情。你需要找到泄漏点。单步绘制代码,同时密切关注 GDI 对象计数器。建议使用好的分析器。 >>非常好的功能,因为它保持原生窗口不变。 【参考方案2】:

获取 Red Gate 的 Memory Profiler 的副本。 http://www.red-gate.com/products/dotnet-development/ants-memory-profiler/

【讨论】:

【参考方案3】:

原来我只是使用任务管理器并尝试重现问题。我们的应用程序(以及 GDI 对象泄漏)的问题是我们使用了一个静态对象并绑定到它。 .net 3.5(至少)存在/存在一个错误,关闭时表单将不知道如何正确处理所有内容,并且某些图形对象将保留在内存中。

【讨论】:

你能提供你的陈述的来源吗?那个错误是提交的吗?我们可以追踪它吗?谢谢 我向 MSDN 提交了一些文件,但它被关闭了,说它不会被修复。但在我的另一个问题 (***.com/questions/8343109/…) 中,我发布了重现该问题的确切代码。【参考方案4】:

您可以使用 perfmon (windows->start->perfmon) 来监控 GDI 泄漏。它能够监控和记录 csv 文件中的数据。

【讨论】:

? GDI 对象似乎没有被性能计数器跟踪:technet.microsoft.com/en-us/library/cc958260.aspx

以上是关于如何调试 GDI 对象泄漏?的主要内容,如果未能解决你的问题,请参考以下文章

处理 Icon 和 Bitmap 有区别吗?

使用GDI时如何确定是否有内存泄漏

如何在 Windows 窗体中创建 GDI 泄漏!

System.Drawing.Bitmap.GetHicon() 上的 GDI 对象泄漏

Upstart init 正在泄漏内存,您如何调试它?

GDI对象泄漏导致程序UI界面绘制异常的问题排查