.net winforms可能存在错误,或者我错过了什么? GDI对象泄漏

时间:2011-12-01 14:41:58

标签: .net winforms gdi

有人可以告诉我出了什么问题吗?

删除大量代码以找到我们的GDI对象泄漏(使用任务管理器并观察“GDI对象”列增长到10,000并且我们的应用程序崩溃)我将代码缩减为仅.net代码而没有任何自定义业务代码。我们仍然在解决这个问题。

我创建了一个测试应用来复制问题,该问题具有以下基本行为。

  • 打开一个表格150次(150没什么特别的,只是一个足以容易看到“卡住”手柄的数字)。表单上的计时器将关闭表单 1秒后
  • 运行垃圾收集器(不是必需的,但可以帮助摆脱不属于问题的“好”或“工作”对象)
  • 手动观察应用程序的GDI对象计数(您应该在打开表单150次之前和之后执行此操作。)在运行测试之前,我通常会计算36,在测试之后它大约是190.我运行测试时,这个数量增加了大约150个。

现在正在以特定方式设置150次启动的表单(让我们调用表单“BadForm”。这是一个静态数据表,它绑定到表单上的组合框。

BadForm上有一个comboBox和一个计时器。以下是表单的代码:

using System;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
namespace GDIObjectLeakTest
{
  public partial class MyForm :Form
  {
    public static DataTable CachedNodeType = new DataTable();

    public MyForm()
    {
      InitializeComponent();
      this.comboBox1.SelectedIndexChanged += new EventHandler(this.comboBox1_SelectedIndexChanged);
      this.Font = new Font("Modern No. 20", 8.249999F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0))); ;
      comboBox1.DataSource = CachedNodeType;
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
      Close();
    }

    private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
    { }
  }
}   

以下是运行测试的应用程序主窗体的代码。它上面有2个按钮。 Button1运行BadForm 150次。按钮2运行垃圾收集器100次(我猜一次或两次对我来说不够好)(我使用垃圾收集器只是为了证明存在/不存在问题)。

private void button1_Click(object sender, EventArgs e)
{
  try
  {
    for(int i = 0; i < 150; i++)
    {
      //new SearchForm().Show();
      new MyForm().Show();
    }
  } catch(Exception ee)
  {
    throw;
  }
}

private void button2_Click(object sender, EventArgs e)
{
  for(int i = 0; i < 100; i++)
  {
    GC.Collect();
    GC.WaitForPendingFinalizers();
  }
}

3 个答案:

答案 0 :(得分:2)

尝试将其添加到dispose方法的顶部(在设计器文件中):

comboBox1.DataSource = null;

答案 1 :(得分:0)

我在这里看到两个潜在的问题:

  1. 如果创建实现IDisposable的对象的实例,则必须在其上调用Dispose方法,或将其包含在using块中。 表单实现IDisposable ,因此您的代码应如下所示:

        using (Form myform = new Form())
        {
            myform.Show();
        }   //frees resource by calling Dispose automatically
    

    否则,你会看到你在这里看到的内存泄漏,因为你正在创建表单的新实例,但是你永远不会释放它的资源。由于终结器在BCL中的写入方式,垃圾收集最终可能会在WinForms中为您释放Windows资源,但调用Dispose会立即执行此操作。

  2. 您正在创建一个新的Font对象,并在每次初始化表单时将其分配给Font属性。并不是说这一定是坏事(设计器生成的代码会大量执行此操作),但每个新的Font实例都占用一个GDI句柄,除非你调用Font.Dispose,否则不会自动释放它。我的猜测是你要留下另一个可能没有正确使用Disposed的Font对象。如果要对这些对象进行巨大的数量,您可能希望以某种方式对其进行优化(例如通过共享Font实例)。 In at least one case,不调用Dispose on fonts会导致内存泄漏。

答案 2 :(得分:0)

您使用的是Font对象,它是一个GDI对象,在您处置它之前不会被释放。 使用Font对象和using语句,或者在FormClose事件上调用Font.Dispose。