c# - 垃圾收集和 GCHandle.Alloc

标签 c# garbage-collection

void Foo()
{
     System.Windows.Forms.Form f = new System.Windows.Forms.Form();
     f.Show();
}

据我了解,f 持有对表单的引用。但 f 是一个局部变量,当控件离开花括号时它将超出范围。但表格仍然开放。我尝试调用 GC.Collect(),但表单仍然打开。

还有一个场景。

private void button2_Click(object sender, EventArgs e)
    {
        Timer t = new Timer();
        t.Enabled = true;
        t.Interval = 1000;
        t.Tick += new EventHandler(t_Tick);
    }

    void t_Tick(object sender, EventArgs e)
    {

    }

在这种情况下,t永远不会被垃圾收集。经过大量研究,我发现当我设置 t.Enabled = true 时,Timer 类正在请求 GC 不要使用 - GCHandle.Alloc 进行收集。伙计们,这是内存泄漏的一个重要来源。除非我设置 t.Enabled = false,否则即使我们关闭 Form,整个 Form 也会被泄露。

在第一个示例代码中,我无法理解为什么即使在触发 GC.Collect() 后表单也没有被垃圾收集。在反射器中,我看到 ControlNativeWindow 已在内部使用 GCHandle.Alloc 的 Form 中使用。这是一个原因吗?作为 .NET 库的用户,我始终相信,当引用无法访问时,它将有机会进行垃圾回收。当然,垃圾收集和内存的实际释放是不确定的。但我的问题是 - 我对这两个例子的理解是否正确?当有些对象即使在无法访问后仍然可以生存时,我将如何跟踪它以防止内存泄漏?

最佳答案

Winforms 保留一个将句柄映射到控件实例的内部表。该表确保只要 native 窗口处于事件状态,控件(您的情况下的表单)就永远不会被垃圾收集。当窗口被销毁时,它会从该表中删除,无论是通过用户关闭表单还是通过代码处理它。

System.Timers.Timer 通过 CLR 引用的 cookie 保持事件状态。该类是用 System.Threading.Timer 实现的,它有一个采用 state 对象参数的构造函数。该state对象就是cookie,CLR使用相当于GCHandle.Alloc()的方法来保持它的引用。禁用计时器会重置 cookie,从而允许计时器被垃圾收集。

这些是框架防止这些对象过早被垃圾收集的自然且必要的方法。只有在处理表单时忘记禁用计时器才会导致泄漏。一般来说,这是非常不健康的,您不希望计时器在表单死亡时继续滴答作响。将 Dispose 方法从 Designer.cs 文件移至表单代码中或重写 OnFormClosed() 以禁用计时器。

关于c# - 垃圾收集和 GCHandle.Alloc,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14617336/

相关文章:

c# - Take() 抛出 Not Implemented Exception Entity Framework 6 with MySQL

c# - oAuth 协议(protocol)中 accessToken 的到期日期

c# - MemoryStream.SetLength(0) 和 MemoryStream.Capacity = 0 不会清除已用内存

c# - Winform窗体关闭事件

c# - 我的类(class)在序列化过程中丢失了方法

c# - 替换文本文件中的文本 c#

java - jmap中显示的 "From Space"和 "To Space"是什么意思?

C# .NET 对象处置

go - Go 是否保证地址不变?

.net - 我对 dumpheap 和 gcroot 的理解不正确吗