我创建了一个如下所示的自定义控件。
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泄漏。
我的问题是,
- 为什么调用了 TextBoxEx 的 Dispose() 方法后,Font 没有被释放。(关闭 Form2 时,会自动调用 TextBoxEx 的 Dispose() 方法)。
- 如何解决Font导致的GDI泄漏问题? (TextBoxEx的Dispose()方法中不能调用Font.Dispose(),因为它会在构造函数中抛出“参数无效”异常)。
最佳答案
大多数绘图对象的创建成本非常。例如,一支钢笔或一支画笔的创建时间不超过一微秒。这就是为什么您应该始终在开始绘图时创建它们并在完成绘图时处理它们。 using 语句强烈鼓励您这样做。
但是 Font 类很困难。它们的创建成本不便宜,Windows 需要做大量工作来将您要求的字体映射到可用的字体集并加载 TrueType 轮廓。 Winforms 有一个解决方案,它缓存字体。第一次使用字体时,您将承担创建字体的费用。但是您可以将其丢弃,但字体对象仍保留在字体缓存中。下次您创建相同的字体时,您将从缓存中获得非常便宜的副本。
这也是 WPF 中的一个问题,而且更是如此,因为它具有更丰富的字体支持,包括对 OpenType 轮廓的支持。这是通过不同的方式解决的,WPF 使用完全独立的进程来缓存字体。充当任何 WPF 应用程序的字体缓存服务器。您将在任务管理器中看到这个进程,它是PresentationFontCache.exe 进程。
无论如何,任何类型的泄漏诊断程序都会被这个缓存混淆。它会认为您的应用程序正在泄漏字体,它会看到存储在缓存中的字体。只有当使用的字体数量无限增长并最终导致程序崩溃时,才会出现真正的泄漏。易于测试,Windows施加的配额较低,一个进程不能创建超过10,000个绘图对象。因此,如果确实存在泄漏,则无需运行测试程序很长时间即可达到该配额。您还可以在任务管理器中看到这一点。查看+选择列,勾选 GDI 对象复选框。确保您的测试程序的数量稳定,并且不会超过数百个。
关于c# - 字体导致自定义控件中的 GDI 泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16261951/