我在Windows应用程序中看到了轻微的内存泄漏。我在我的应用程序中使用DevExpress XtraForm。我看到的是表单的一个实例总是保存在内存中。如果多次打开同一个表单,它仍会保持打开最后一个表单的引用。
实施例。如果你在应用程序中打开10个不同的表单并关闭所有表单,它仍然不会释放分配给它的内存,因为一些奇怪的“MdiClient对象引用了LayoutEventArgs对象”。幸运的是,它保留了每种类型单项的参考。
这是Redgate内存分析器输出的链接。
https://dl.dropboxusercontent.com/u/2781659/Memory%20Leak.pdf
在上面的图表中,DepartmentsForm被放置但由于LayoutEventArgs的affectedComponent成员引用它而无法进行GC。
如果您发现任何明显错误,请告知。
答案 0 :(得分:8)
根据我的经验,在Windows窗体中存在一些情况,当处置控件可以缓存在LayoutEventArgs
对象中时,它看起来像WinForms中的某种小错误。
一些细节:
System.Windows.Forms.Control
类型的每个实例都包含LayoutEventArgs
类型的私有成员变量 - cachedLayoutEventArgs
。并且,LayoutEventArgs
通常包含对某些特定控件的引用。您可以通过Reflector清楚地看到所有这些事实。并且,有时,当子控件处理因某些原因而不影响父控件的布局过程时,cachedLayoutEventArgs
字段不会被清除。您可以通过在关闭子项时暂停MdiClient的控件布局来使用mdi父窗体来模仿这种情况:
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;
}
}
}
P.S。无论如何,我建议您在这方面联系DevExpress support,以确保您描述的内存泄漏与其控件无关,并获得最终解决方案。