Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/github/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
可能是.net winforms存在漏洞,还是我遗漏了什么?GDI对象泄漏_.net_Winforms_Gdi - Fatal编程技术网

可能是.net winforms存在漏洞,还是我遗漏了什么?GDI对象泄漏

可能是.net winforms存在漏洞,还是我遗漏了什么?GDI对象泄漏,.net,winforms,gdi,.net,Winforms,Gdi,有人能告诉我出了什么问题吗 在删除了大量查找GDI对象泄漏的代码后(使用任务管理器,看着“GDI对象”列增长到10000,我们的应用程序崩溃),我将代码减少到只有.net代码,没有任何自定义业务代码。我们仍在处理这个问题 我创建了一个测试应用程序来复制这个问题,它具有以下基本行为 打开表单150次(150没有什么特别的,只是一个足够大的数字,可以很容易地看到“卡住”的句柄)。窗体上的计时器将关闭窗体 1秒钟后 运行垃圾回收器(实际上没有必要,但可以帮助清除不属于问题一部分的“良好”或“工作”对

有人能告诉我出了什么问题吗

在删除了大量查找GDI对象泄漏的代码后(使用任务管理器,看着“GDI对象”列增长到10000,我们的应用程序崩溃),我将代码减少到只有.net代码,没有任何自定义业务代码。我们仍在处理这个问题

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

  • 打开表单150次(150没有什么特别的,只是一个足够大的数字,可以很容易地看到“卡住”的句柄)。窗体上的计时器将关闭窗体 1秒钟后
  • 运行垃圾回收器(实际上没有必要,但可以帮助清除不属于问题一部分的“良好”或“工作”对象)
  • 手动观察应用程序的GDI对象计数(您应该在打开表单150次之前和之后进行此操作。)在运行测试之前,我通常得到36个计数,测试之后大约是190个。每次我运行测试时,这个计数都会增加大约150
现在,正在启动150次的表单是以一种特定的方式设置的(让我们调用表单“BadForm”),它是一个静态数据表,绑定到表单上的一个组合框

BadForm上有一个组合框和一个计时器。下面是表单的代码:

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)
    { }
  }
}   
下面是运行测试的应用程序主窗体的代码。它有两个按钮。按钮1运行坏窗体150次。按钮2运行垃圾收集器100次(我想一次或两次对我来说还不够好)(我使用垃圾收集器只是为了证明有/没有问题)

private void按钮1\u单击(对象发送者,事件参数e)
{
尝试
{
对于(int i=0;i<150;i++)
{
//新建SearchForm().Show();
新建MyForm().Show();
}
}捕获(异常ee)
{
投掷;
}
}
私有无效按钮2\u单击(对象发送者,事件参数e)
{
对于(int i=0;i<100;i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}

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

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

        using (Form myform = new Form())
        {
            myform.Show();
        }   //frees resource by calling Dispose automatically
    
    否则,您将看到此处出现的内存泄漏,因为您正在创建表单的新实例,但却从未释放表单的资源。垃圾收集可能最终会在WinForms中为您释放Windows资源,这是由于在BCL中写入终结器的方式,但调用Dispose将立即完成

  • 您正在创建一个新的
    Font
    对象,并在每次初始化表单时将其分配给Font属性。这并不一定是坏的(设计师生成的代码会大量执行此操作),但每个新的
    Font
    实例都占用一个GDI句柄,除非调用Font.Dispose,否则不会自动释放该句柄。我猜您将留下另一个可能无法正确处理的字体对象。您可能希望以某种方式对此进行优化(例如通过共享字体实例)如果您正在对这些对象进行大量的,不在字体上调用Dispose将导致内存泄漏


  • 您正在使用字体对象,它是一个GDI对象,在您处置它之前不会被处置。
    在using语句中使用Font对象,或者在FormClose事件中调用Font.Dispose。

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


    当您使用完
    MyForm
    后,您需要调用
    Dispose
    。您不应该依赖垃圾收集器来进行清理。我不认为这是问题所在,好像我更改了设置中的任何一件事,一切都如我所期望的那样工作(从基本表单中删除字体,或删除对数据源的分配,或将分配更改为datatable.copy(),或删除事件处理程序。)所有这些事情都在改变,所有GDI句柄都被正确返回。我在测试中调用垃圾收集器的原因是,如果一切正常,由于垃圾收集器尚未运行,句柄可能不会立即返回。发生这种情况时,我无法判断问题是否存在。是否有其他代码你没有给我们看吗?根据显示的代码,表单是我能看到的唯一会泄漏的GDI对象,正确处理它们可以防止泄漏。我没有显示设计器文件。这与字体无关,只是我正在指定字体。我正在做的是更改字体的标准方式这里唯一不同的地方(我这样做不是为了让别人更容易消化)将代码从designer.cs移到.cs。记住,如果您更改了这三件事中的任何一件,一切都会正常工作。这不仅仅是导致问题的原因之一,而是两者的组合。这就是字体对象在设计器文件中创建和使用的方式。我只是总结了发生的情况并将其格式化为这样。错误是s与静态对象和表单无法自行处理有关..我感谢您的帮助。我早已不再处理此问题(自发布此帖子以来,我已移动了项目和公司)但我仍然会对此发表评论。我不喜欢编辑设计器文件。这是一个非常脆弱的更改。VS将经常重写整个文件,很难判断该行是意外删除的。我觉得我在这里遇到的问题是静态对象的绑定。我可能应该在perha中尝试您的行建议ps关闭/关闭事件。这可能会解决此问题。
        using (Form myform = new Form())
        {
            myform.Show();
        }   //frees resource by calling Dispose automatically
    
    comboBox1.DataSource = null;