winforms 当用户关闭一个非模态窗口时,如何确保垃圾回收?

5sxhfpxr  于 2023-01-14  发布在  其他
关注(0)|答案(3)|浏览(173)

在我的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%。
我听到你们说的了,但是..........我想我会将代码保留在"Run#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);
    }
}
lnvxswe2

lnvxswe21#

我还担心在用户打开和关闭Form2时可能发生的内存泄漏。
为什么要担心内存泄漏?在没有问题的迹象时不要尝试优化。只要Form2及其所有子对象在调用Dispose时实际清理资源,就不应该有内存泄漏。
有没有更好的方法来确保在Form2关闭时进行垃圾收集?我不想依赖Windows在以后决定这样做时进行垃圾收集
这看起来像是不必要的妄想症。只要确保Form2在其Dispose方法中进行清理,并让垃圾收集自然发生。除了删除GC.Collect()调用之外,一切看起来都很好。

wvt8vs2t

wvt8vs2t2#

Form2的成员示例不会占用堆上的很多空间,而且似乎没有什么理由在每次用户想要显示它时创建和销毁它的Handle。

为什么不在应用程序关闭之前阻止Form2句柄的破坏呢?

public partial class Form2 : Form
{
    public Form2(Form owner)
    {
        InitializeComponent();
        Owner = owner;
        StartPosition = FormStartPosition.Manual;
    }
    protected override void OnVisibleChanged(EventArgs e)
    {
        base.OnVisibleChanged(e);
        if(Visible)
        {
            Location = new Point(
                Owner.Location.X + Owner.Width + 10,
                Owner.Location.Y);
        }
    }
    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        base.OnFormClosing(e);
        if(e.CloseReason.Equals(CloseReason.UserClosing))
        {
            e.Cancel = true;
            Hide();
        }
    }
}

当表格2循环(通过自动化)100次时,监测过程内存显示GC为零,并且与所示次数无直接相关性。

其中:

public partial class Form1 : Form
{
    Form2 _form2;
    public Form1()
    {
        InitializeComponent();
        _form2 = new Form2(this);
        Disposed += (sender, e) => _form2.Dispose();
        buttonShowHelp.Click += async (sender, e) =>
        {
            for (int i = 0; i < numericUpDown.Value; i++)
            {
                _form2.Visible = true;
                await Task.Delay(500);
                _form2.Visible = false;
                await Task.Delay(500);
            }
            // But leave it open after cycling.
            _form2.Visible = true;
        };
    }
    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        base.OnFormClosing(e);
    }
}
b5lpy0ml

b5lpy0ml3#

如果你把垃圾放在厨房垃圾桶里,5分钟后垃圾还在那里,这并不意味着垃圾会永远在那里。这只是意味着垃圾还没有被清空。一旦垃圾桶装满,它最终会被清空。“泄漏”是指如果你把垃圾放在地板上,在这种情况下,当垃圾桶装满时,它就不会被捡起来。(不是一个完美的类比,因为有人(希望)会在某个时候捡起来,但你明白这个想法)
您的观察结果可能会显示100%的增长,因为没有内存压力需要进行收集。只有当内存“从未”被GC释放时,才会发生内存泄漏,强制垃圾收集无法修复这种情况。内存泄漏的一个示例是对非托管资源的引用,该资源在释放(未关闭)窗体时没有释放。
自己调用GC.Collect并不能修复内存泄漏--它只是在系统需要的时候 * 更早地 * 清理内存。

相关问题