c# - Windows 窗体内存泄漏

标签 c# .net memory-leaks devexpress devexpress-windows-ui

我在我的 Windows 应用程序中看到轻微的内存泄漏。我在我的应用程序中使用 DevExpress XtraForm。我看到的是表单的一个实例始终保存在内存中。如果您多次打开同一个表单,它仍然会保留上一个打开的表单的引用。

例。如果您在应用程序中打开 10 个不同的窗体并关闭所有窗体,它仍然不会释放分配给它的内存,因为一些奇怪的“MdiClient 对象引用 LayoutEventArgs 对象”。幸运的是,它保留了每种类型的单个项目的引用。

这是指向 Redgate 内存分析器输出的链接。

https://dl.dropboxusercontent.com/u/2781659/Memory%20Leak.pdf

在上面的图表中,DepartmentsForm 被处置但不能被 GC,因为 LayoutEventArgs 的 affectedComponent 成员引用它。

如果您看到任何明显的错误,请提出建议。

最佳答案

根据我的经验,在 Windows 窗体中存在一些情况,当释放的控件可以缓存在 LayoutEventArgs 对象中时,它看起来像是 WinForms 中的某种小错误。

一些细节:
System.Windows.Forms.Control 类型的每个实例都包含一个 LayoutEventArgs 类型的私有(private)成员变量 - cachedLayoutEventArgs。而且,LayoutEventArgs 通常包含对某些特定控件的引用。您可以通过 Reflector 清楚地看到所有这些事实。并且,有时由于某些原因子控件的处理不影响父控件的布局过程时,cachedLayoutEventArgs字段没有被清除。您可以使用 mdi 父窗体通过在关闭其子窗体时暂停 MdiClient 的控件布局来模拟这种情况:

public partial class MdiParentForm : Form {
    public MdiParentForm () {
        InitializeComponent(); //  this.IsMdiContainer = true
    }
    void buttonAddMdiChild_Click(object sender, EventArgs e) {
        MdiChildForm f = new MdiChildForm();
        f.MdiParent = this;
        f.Show();
    }
    void buttonCloseMdiChild_Click(object sender, EventArgs e) {
        MdiClient client = GetMdiClient(this);
        client.SuspendLayout();

        if(ActiveMdiChild != null)
            ActiveMdiChild.Close();

        client.ResumeLayout(false); 
        // !!! At this point the MdiClient.cachedLayoutEventArgs contains the reference to disposed control (leak)
    }
    static MdiClient GetMdiClient(Form frm) {
        if(frm != null) {
            foreach(Control ctrl in frm.Controls) {
                if(ctrl is MdiClient)
                    return (MdiClient)ctrl;
            }
        }
        return null;
    }
}
class MdiChildForm : Form { }

有一个简单的解决方法 - 通过触发 PerformLayout 方法,您可以有效地清除“缓存”实例:

class MdiChildForm : Form {
    MdiClient parent;
    protected override void OnParentChanged(EventArgs e) {
        base.OnParentChanged(e);
        var mdiClient = Parent as MdiClient;
        if(mdiClient != parent) {
            if(parent != null)
                parent.PerformLayout();
            parent = mdiClient;
        }
    }
}

附言我建议您以任何方式联系 DevExpress support对此,确保你描述的内存泄漏与他们的控件无关,得到最终的解决方案。

关于c# - Windows 窗体内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25181679/

相关文章:

c# - 获取小数点的最后(即结尾)3 位数字 (.NET)

Java内存泄漏示例

c# - .Net 4.5.1 ConcurrentDictionary TryRemove() 方法是否释放分配的内存?

c# - 使用 "using"时的意外行为

c# - 单元测试方法的有效输入

java - 线程将随着时间的推移而更新,以尝试避免可能的内存泄漏

c# - 如何将 ModelExpression 绑定(bind)到 ASP.NET Core 中的 ViewComponent?

c# - 来自身份服务器连接\ token 的 token 对我的 API 无效

c# - 两次加载相同的程序集但版本不同

c# - 转换方法。 "The specified method on the type cannot be translated into a LINQ to Entities store expression"