Windows窗体中的C#垃圾收集

时间:2017-04-25 06:40:13

标签: c# garbage-collection

我有两个窗口形式frmMain& frmDialog。当我点击frmMain上的按钮时,frmDialog打开。这是我的按钮点击事件的代码:

frmDialog f2 = new frmDialog();

f2.Show();

当我继续点击按钮时,会出现新的表单。 不要关闭。当一个对象超出范围时,它会被垃圾收集。

我的问题是:

为什么变量f2在超出范围时会收集垃圾?

是内存泄漏吗?

3 个答案:

答案 0 :(得分:1)

有一个技巧。当您致电Show时,属性Visible设置为true,然后调用SetVisibleCorehttp://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Form.cs,b31b076f655b0b4b

有趣的是:

if (CalledOnLoad) {
    // VSWhidbey 518085: make sure the form is in the Application.OpenForms collection
    if (!Application.OpenFormsInternal.Contains(this)) {
        Application.OpenFormsInternalAdd(this);
    }
}

表单将添加到存储在静态属性Application.OpenFormsInternal中的集合中。因此,您的表单会生根,并且在关闭之前不会有资格进行垃圾回收(并自动从该集合中删除)。

根据Hans Passant的评论,我玩了一些内存转储:

0:000> !gcroot 000000000263d368
HandleTable:
0000000000191348 (strong handle)
-> 000000000263d368 WindowsFormsApp1.Form1

00000000001917d0 (pinned handle)
-> 00000000125f99a8 System.Object[]
-> 000000000262c9a8 System.Windows.Forms.FormCollection
-> 000000000262c9d8 System.Collections.ArrayList
-> 000000000262ca00 System.Object[]
-> 000000000263d368 WindowsFormsApp1.Form1

我们看到Application.OpenFormsInternal集合(第二个根),但也是一个强大的句柄,即使它不存在于集合中(apparently could happen),也会使表单保持活动状态。< / p>

答案 1 :(得分:0)

是的,这是内存泄漏。

在C#中你有两种内存。托管内存和非托管内存。托管内存由GC管理:)非托管内存不是。你必须自己释放非托管内存。例如(托管):

{
    List<int> l = new List<int(); 
}

这个不会造成内存泄漏。 l 超出范围后,标记为已释放。 标记为未释放。它可以在以后释放(然后将调用列表析构函数)。

那么非托管内存怎么样?

{
    MyForm f = new MyForm();
}

这是内存泄漏。当 f 超出范围时,其非托管内存根本不受GC管理。所以你必须自己释放

{
    MyForm f = new MyForm();
    f.ShowDialog();
    f.Dispose();
}

Dispose方法释放所有未管理的内存。所以现在你可能有两个问题:  1.我如何知道课程是管理还是不管理? 这很简单。您可以假设所有实现IDisposable接口(包含Dispose()方法)的类都是非托管的 - 它们本身不受管理,或者由非托管类组成。特别是每个资源,如字体,流,表单,数据库连接等。

  1. 我应该如何免费模式? 您可以存储对该表单的引用,并在不再需要时将其处理。您可以在FormClosed事件(https://msdn.microsoft.com/en-us/library/system.windows.forms.form.formclosed.aspx
  2. 中调用Dispose

答案 2 :(得分:-1)

为什么你认为对象超出范围或应该这样做?表单将作为无模式对话框打开,并且一直存在,直到您(用户)关闭它。