Windows窗体内存泄漏 [英] Windows Form Memory leak

查看:204
本文介绍了Windows窗体内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Windows应用程序中看到一个轻微的内存泄漏。我在应用程序中使用DevExpress XtraForm。我看到的一种形式的实例总是被保留在记忆中。如果您打开相同的表单多次,它仍然保持打开的最后一个表单的引用。



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



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



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



在上图中,DepartmentsForm被淹没,但由于affectedComponent成员不能被GCed的LayoutEventArgs引用它。



请告知您是否看到任何明显错误。

解决方案

根据我的经验,在Windows Forms中有一些情况,当处理的控件可以缓存在 LayoutEventArgs 对象中,并且它看起来像WinForms中的一些小错误。



某些详细信息:

System.Windows.Forms.Control 类型的每个实例包含 LayoutEventArgs 类型的私有成员变量 - cachedLayoutEventArgs 。而且, LayoutEventArgs 通常包含对某些特定控件的引用。您可以通过反射器清楚地看到所有这些事实。而且,有时候,由于某些原因,子控件处理不会影响父控件的布局过程,因此 cachedLayoutEventArgs 字段不会被清除。您可以使用mdi父表单通过暂停MdiClient的控件布局来关闭其子项来模仿这种情况:

  public partial class MdiParentForm:表单{
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);
customer.SuspendLayout();

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

client.ResumeLayout(false);
// !!!此时,MdiClient.cachedLayoutEventArgs包含对处理的控件(泄漏)的引用
}
static MdiClient GetMdiClient(Form frm){
if(frm!= null){
foreach (控制ctrl在frm.Controls){
如果(ctrl是MdiClient)
返回(MdiClient)ctrl;
}
}
返回null;
}
}
class MdiChildForm:Form {}

是一个简单的解决方法 - 通过触发 PerformLayout 方法,您可以有效地刷新缓存实例:

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

无论如何,我建议您在这方面联系 DevExpress支持,以确保内存泄漏你描述的与他们的控制无关,并得到最终解决方案。


I see a slight memory leak in my windows application. I use DevExpress XtraForm in my application. What I see is one instance of the form is always being kept in memory. If you open the same form multiple times it still keeps the reference of last form opened.

Ex. if you open 10 different form in the application and close all of them it would still not release the memory assigned to it because of some weird "MdiClient object references LayoutEventArgs object". Fortunately it keep reference of single item per type.

Here is the link to the Redgate memory profiler output.

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

In the chart above the DepartmentsForm is diposed but cannot be GCed because of affectedComponent member of LayoutEventArgs referencing it.

Please advise if you see any obvious error.

解决方案

From my experience there are some situation in Windows Forms when disposed controls can be cached within the LayoutEventArgs object and it looks like some kind of minor bug in WinForms.

Some details:
Each instance of the System.Windows.Forms.Control type contains a private member variable of the LayoutEventArgstype - cachedLayoutEventArgs . And, the LayoutEventArgs typically contains a reference to some specific control. You can clearly see all of these facts via Reflector. And, sometimes, the cachedLayoutEventArgs field is not cleared when the child control disposing does not affect the layout process of the parent control sue to some reasons. You can imitate this situation using the mdi parent form by suspending the MdiClient's control layout while closing its children:

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 { }

There is a simple workaround - by triggering the PerformLayout method, you can effectively flush-out that "cached" instance:

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;
        }
    }
}

P.S. In any way I suggest you contact the DevExpress support in this regard, to sure that the memory leak you described is not related to their controls and get the final solution.

这篇关于Windows窗体内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆