【问题标题】:C# - ShowDialog(this) memory leakC# - ShowDialog(this) 内存泄漏
【发布时间】:2014-01-17 22:33:15
【问题描述】:

我有显示新表单对话框的示例代码:

private void button1_Click(object sender, EventArgs e)
{
    (new Form2()).ShowDialog(this);
    GC.Collect();
}

如果表单有按钮、面板标签等,则执行此 form2 的 Dispose 方法,但如果我添加 toolStrip,则不会执行 Dispose 方法。为什么在这些情况下会执行 Dispose?

我读到如果 ShowDialog 显示表单,那么我应该执行 Dispose 方法,但为什么有时没有它它可以工作?

编辑: 可以在 ShowDialog 之后添加 Collect 方法。此方法仅用于测试,会多次执行。

为了检查方法 Dispose 是否被执行,我添加了断点(在调试模式下)。当然在程序结束时执行带有 ToolStrip 的示例。

好的,我知道如何正确实现它,我很感兴趣,如果表单上是 toolStrip,为什么 GC 无法清理?

显示它的最简单的代码是: 示例 1 - 结果 - 100, 示例 2 - 结果 > 0(GC 可以清理), 示例 3 - 始终为 0。 为什么示例 2 和示例 3 如此不同?

private class Form2 : Form
    {
        public static int disposed = 0;    
        byte[] data;
        private System.Windows.Forms.ToolStrip toolStrip11;
        public Form2(bool addToolStrip)
        {
            data = new byte[100000];
            this.Shown += (sender, e) => { this.Close(); };
            this.Controls.Add(new Button());

            if (addToolStrip)
            {
                this.toolStrip11 = new System.Windows.Forms.ToolStrip();
                this.Controls.Add(this.toolStrip11);
            }
        }

        protected override void Dispose(bool disposing)
        {
            ++disposed;
            base.Dispose(disposing);
        }
    }
    private void ShowResult()
    {
        GC.Collect(); GC.Collect();
        GC.WaitForFullGCComplete();
        MessageBox.Show(Form2.disposed.ToString());
        Form2.disposed = 0;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        //proper
        for (int i = 0; i < 100; ++i)
        {
            using(Form2 f = new Form2(true))
            {
                f.ShowDialog();
            }
        }
        ShowResult();
        //ok GC can clean - why yes ?
        for (int i = 0; i < 100; ++i)
        {
            Form2 f = new Form2(false);
            f.ShowDialog();
        }
        ShowResult();
        //GC can't clean - why not ?
        for (int i = 0; i < 100;a ++i)
        {
            Form2 f = new Form2(true);
            f.ShowDialog();
        }
        ShowResult();
    }

【问题讨论】:

  • 我真的不明白你的代码试图做什么。您调用 Collect 时没有在该范围内创建任何对象(或删除对所述对象的引用)。如果您想调用Dispose 方法,请在using 语句中声明您的对话。
  • “为什么在这些情况下会执行 Dispose?” - 您如何确定 Dispose() 会在您的 Form2 上被调用?

标签: c# .net winforms memory-leaks


【解决方案1】:

您的代码没有意义 - 您在进行声称泄漏内存的调用之前强制执行垃圾回收周期。

您应该只使用implement Dispose to get rid of unmanaged resources。对于托管资源,尝试在任何常见用例中击败垃圾收集器根本没有意义。此外,我不确定您是如何认为自己在“内存泄漏”,因为您无法预测或知道 GC 何时正确完成其工作 - 严格来说,.NET 程序只有一个如果 GC 拒绝 正确清理它,则真正的内存泄漏。由于这是未向开发人员公开的信息 - 即使是明确的清理也可以自行决定不收集所有潜在的垃圾 - 根据定义,您的声明是无法验证的。

【讨论】:

  • Dispose 应该被实施以强加某些东西的有限生命周期,并且不一定非要针对非托管资源;您的限定词“仅”不合适,并且您链接到的文章不支持。包装非托管资源的类可能应该实现 Dispose,但这不是应该使用 Dispose 的唯一情况。
【解决方案2】:

许多一次性对象也有终结器。当一个对象通常被 GC 清理时,它会首先检查它是否有一个尚未运行的终结器。如果没有,它会进入队列等待其终结器运行,然后实际上有资格进行垃圾回收。

由于这种机制,一些一次性对象可能会清理它们的资源,即使它们没有被明确地释放。

也就是说,这是一种不可靠的机制。你不知道一个对象一旦符合收集条件就会被收集,并且当终结器运行时对象所处的状态可能会导致与“自身”的异常和未定义的交互。

您应尽可能避免依赖终结器,而是明确处置此类一次性资源。

【讨论】:

    猜你喜欢
    • 2016-01-27
    • 2010-11-11
    • 2017-02-18
    • 1970-01-01
    • 2015-11-19
    • 2014-11-01
    • 2020-03-31
    • 2016-03-15
    • 2015-07-25
    相关资源
    最近更新 更多