我有两个窗口形式frmMain& frmDialog。当我点击frmMain上的按钮时,frmDialog打开。这是我的按钮点击事件的代码:
frmDialog f2 = new frmDialog();
f2.Show();
当我继续点击按钮时,会出现新的表单。 不要关闭。当一个对象超出范围时,它会被垃圾收集。
我的问题是:
为什么变量f2在超出范围时会收集垃圾?
是内存泄漏吗?
答案 0 :(得分:1)
有一个技巧。当您致电Show
时,属性Visible
设置为true,然后调用SetVisibleCore
:http://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()方法)的类都是非托管的 - 它们本身不受管理,或者由非托管类组成。特别是每个资源,如字体,流,表单,数据库连接等。
答案 2 :(得分:-1)
为什么你认为对象超出范围或应该这样做?表单将作为无模式对话框打开,并且一直存在,直到您(用户)关闭它。