2

我创建了一个如下所示的自定义控件。

public partial class TextBoxEx : TextBox
{
  public TextBoxEx()
  {
    InitializeComponent();
    Font = Utility.normalFont;
  }

   protected override void OnPaint(PaintEventArgs pe)
   {
    base.OnPaint(pe);
   }
 }
//A utility class to initialize font.   
class Utility
{

    internal static Font normalFont = new Font("Arial", 18);
}

我有两个表格 Form1 和 Form2。此 TextBoxEx 被添加到 Form2。单击 Form1 中的按钮时,我正在显示 Form2。

连续显示和关闭 Form2 会导致我的应用程序中的 GDI 泄漏。用GDI检测工具(Bear.exe)分析,发现是Font导致GDI泄漏。

我的问题是,

  1. 为什么调用 TextBoxEx 的 Dispose() 方法却不释放字体。(关闭 Form2 时,会自动调用 TextBoxEx 的 Dispose() 方法)。
  2. 如何解决由字体引起的 GDI 泄漏?(Font.Dispose() 不能在 TextBoxEx 的 Dispose() 方法中调用。因为它在构造函数中抛出“参数无效”异常)。
4

1 回答 1

3

大多数绘图对象的创建成本都非常低。例如,一支笔或刷子的创建时间不会超过一微秒。这就是为什么你应该总是在开始绘图时创建它们并在你完成绘图时处理它们。using语句强烈建议您这样做。

然而,字体类是困难的。创建它们并不便宜,Windows 需要做很多工作才能将您要求的字体映射到可用的字体集并加载 TrueType 轮廓。Winforms 有一个解决方案,它缓存字体。您将在第一次使用字体时产生创建字体的成本。但是您可以随后处理它,但字体对象仍保留在字体缓存中。下次您创建相同的字体时,您将从缓存中获得非常便宜的副本。

这也是 WPF 中的一个问题,尤其是因为它具有更丰富的字体支持,包括对 OpenType 轮廓的支持。它以不同的方式解决,WPF 使用完全独立的进程来缓存字体。充当任何 WPF 应用程序的字体缓存服务器。您将在任务管理器中看到此进程,它是 PresentationFontCache.exe 进程。

Anyhoo,任何类型的泄漏诊断程序都会被这个缓存混淆。它会认为您的应用程序正在泄漏字体,它会看到存储在缓存中的字体。只有当使用的字体数量无限增长并最终导致程序崩溃时,您才会真正发生泄漏。易于测试,Windows 强加的配额很低,一个进程不能创建超过 10,000 个绘图对象。因此,如果您有真正的泄漏,您不需要运行您的测试程序很长时间即可达到该配额。您还可以在任务管理器中看到这一点。查看 + 选择列,勾选 GDI 对象复选框。确保您的测试程序的数量是稳定的,并且不超过几百个,给予或接受。

于 2013-04-28T11:56:26.450 回答