【问题标题】:How to ensure garbage collection when user closes a non-modal window?用户关闭非模态窗口时如何确保垃圾回收?
【发布时间】:2023-01-12 03:45:30
【问题描述】:

在我的 C# Winforms 应用程序中,我有以下内容(显示的代码最少)

Form1 是用户用来做事的主要应用程序。 Form2 显示了一个帮助文件,解释了如何使用 Form1 上的功能来做事。只要 Form1 可见,我希望用户能够随意显示(无模式)和关闭帮助文件。

我还担心用户打开和关闭 Form2 时可能发生的内存泄漏。因此,当用户关闭 Form2 时,它会引发 Form1 订阅的事件。当调用 Form1 事件方法时,它调用 Form2 上的 Dispose(),将 Form2 对象设置为 null 并调用垃圾收集器。

这会消除因用户打开和关闭 Form2 而导致的内存泄漏的可能性吗?是不是矫枉过正?是否有更好的方法确保在 Form2 关闭的时间点进行垃圾回收?我不想依赖 Windows 稍后决定这样做

更新

Jimi 指出我不需要 Form2 Closed 事件的自定义事件处理程序。他是对的。在我的 Form1 类中,我现在为我的 Form2 使用标准的 FormClosedEventHandler。然而,代码本身几乎保持不变。但是,当我删除对 GC.Collect() 的调用时,我看到了使用任务管理器内存泄漏的证据。

这是我的数据:

Run #1. Form2_FormClosed method has:
------------------------------------
f_HelpForm.Dispose();
f_HelpForm = null;
GC.Collect();

Start App
Task Manager Memory for app: 6.7 MB
Open and Close Form2 20 times
Task Manager Memory for app: 8.2 MB

Run #2. Form2_FormClosed method has:
------------------------------------
f_HelpForm.Dispose();
f_HelpForm = null;
//GC.Collect();

Start App
Task Manager Memory for app: 6.9 MB
Open and Close Form2 20 times
Task Manager Memory for app: 18.9 MB

Run #3. Form2_FormClosed method has:
------------------------------------
//f_HelpForm.Dispose();
f_HelpForm = null;
//GC.Collect();

Start App
Task Manager Memory for app: 6.9 MB
Open and Close Form2 20 times
Task Manager Memory for app: 18.1 M

在没有调用 GC.Collect() 的情况下,无论是否调用 Dispose(),与包含调用 GC.collect() 的代码相比,占用空间增加了 100%。

我听到你们在说什么,但是............我想我会把我的代码留在它的“运行#1”配置中

代码

注意:我承认设置 form2 = null 对幕后垃圾收集有直接影响。但是,我设置 form2 = null 的目的是向 Form2Button_Click 方法提供一个信号,它可以用来决定是否实例化一个 Form2 对象

public partial class Form1 : Form
{
    Form2 form2;
    
    public Form1()
    {
        form2 = null;
    }  

    private void Form2Button_Click(object sender, EventArgs e)
    {
        if (form2 == null)
        {
            form2 = new ClsHelpForm(this);
            form2.Form2Closed += Form2_FormClosed;
        }

        form2.Select();
        form2.Show();
    }

    //When this user clicks the Help button on Form1, this method is invoked
    private void Form2_FormClosed(object sender, EventArgs e)
    {
        form2.Form2Closed -= Form2_FormClosed;
        form2.Dispose();
        form2 = null;
        GC.Collect();
    }   
{

public partial class Form2 : Form
{
    public event EventHandler Form2Closed;

    public Form2()
    {
    }

    //When the user clicks the "X" button on Form2, this method is invoked
    private void Form2_FormClosed(object sender, Form2EventArgs e)
    {
        Form2Closed?.Invoke(this, EventArgs.Empty);
    }
}

【问题讨论】:

  • 你根本不需要public event EventHandler Form2Closed;,你可以在没有它的情况下订阅FormClosed事件。引发标准事件时删除 Form1 中的处理程序。仅此而已——您是否注意到在关闭 Form2 时使用分析器(例如诊断工具)未回收某些内存? -- GC.Collect(); 没有任何目的:它要么可以收集可收集的东西,要么不能。如果可以,它会在需要时执行。
  • 强制 GC 永远不会修复“内存泄漏”(从技术上讲,这在纯 C# 安全代码中不存在)...您可能正在谈论某种长期存在的引用,这些引用也不会被 GC 清除。
  • @cj.burrow @Jimi:我同意我不需要自定义事件处理程序。在我的 Form1 课程中,我现在为我的 Form2 使用标准的 FormClosedEventHandler。但是,当我删除对 GC.Collect() 的调用时,我看到了使用任务管理器内存泄漏的证据。我会尽快用我的发现更新我的问题
  • 您不应该使用 Taskmanager 进行分析。它可能有助于大规模比较资源使用情况与其他进程,但仅此而已。如果您怀疑存在泄漏之类的问题,请使用真正的探查器,例如 Visual Studio 中可用的诊断工具。

标签: c# winforms garbage-collection non-modal


【解决方案1】:

我还担心用户打开和关闭 Form2 时可能发生的内存泄漏。

你为什么担心内存泄漏?当没有证据表明存在问题时,不要尝试优化。只要 Form2 及其所有子对象在调用 Dispose 时实际清理资源,就不会有内存泄漏。

是否有更好的方法确保在 Form2 关闭的时间点进行垃圾回收?我不想依赖 Windows 稍后决定这样做

这似乎是不必要的偏执狂。只要确保 Form2 在其 Dispose 方法中进行清理并让垃圾回收自然发生即可。除了删除 GC.Collect() 调用外,一切看起来都很好。

【讨论】:

  • 并不是说基于传统智慧你是错的,但我无法解释我的经验观察。请在我更新的问题中查看我的测试数据。
【解决方案2】:

如果您将垃圾放入厨房垃圾桶,5 分钟后垃圾仍然在那里,这并不意味着垃圾会永远在那里。这只是意味着垃圾还没有被清空.它会被清空最终一旦它足够满。如果你把垃圾放在一起,就会出现“泄漏”在地上, 在这种情况下不是当垃圾桶装满时被捡起。 (不是一个完美的类比,因为有人(希望)会在某个时候拿起它,但你明白了)

您的观察结果可能显示 100% 的增长,因为没有内存压力要求收集。内存泄漏只会发生,如果该内存绝不被 GC 释放,强制垃圾收集无法修复。内存泄漏的一个示例是对非托管资源的引用,该资源在处理(未关闭)表单时未释放。

自己调用GC.Collect 不会修复内存泄漏——它只会清理内存早些时候系统需要。

【讨论】:

    猜你喜欢
    • 2015-06-10
    • 1970-01-01
    • 1970-01-01
    • 2013-07-12
    • 1970-01-01
    • 2015-02-20
    • 1970-01-01
    • 1970-01-01
    • 2010-10-20
    相关资源
    最近更新 更多