我在我的 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/