Утечка памяти форм Windows

Я вижу небольшую утечку памяти в моем приложении Windows. Я использую DevExpress XtraForm в своем приложении. Я вижу, что один экземпляр формы всегда хранится в памяти. Если вы открываете одну и ту же форму несколько раз, она по-прежнему сохраняет ссылку на последнюю открытую форму.

Бывший. если вы откроете 10 разных форм в приложении и закроете их все, оно все равно не освободит выделенную ему память из-за какого-то странного «объекта MdiClient ссылается на объект LayoutEventArgs». К счастью, он сохраняет ссылку на один элемент для каждого типа.

Вот ссылка на выходные данные профилировщика памяти Redgate.

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

На приведенной выше диаграмме форма DepartmentsForm размещена, но не может быть проверена сборщиком мусора из-за того, что на нее ссылается член затронутого компонента LayoutEventArgs.

Пожалуйста, сообщите, если вы видите какую-либо очевидную ошибку.


person Paresh Varde    schedule 07.08.2014    source источник
comment
Невозможно сказать без кода, даже тогда я подозреваю, что может быть трудно помочь   -  person Sayse    schedule 07.08.2014
comment
Я предоставил график удержания объекта вместе с проблемой.   -  person Paresh Varde    schedule 07.08.2014


Ответы (1)


По моему опыту, в Windows Forms бывают ситуации, когда удаленные элементы управления могут кэшироваться внутри объекта LayoutEventArgs, и это выглядит как какая-то незначительная ошибка в WinForms.

Некоторые подробности:
Каждый экземпляр типа System.Windows.Forms.Control содержит закрытую переменную-член типа LayoutEventArgscachedLayoutEventArgs . И 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;
        }
    }
}

P.S. В любом случае я предлагаю вам обратиться в поддержку DevExpress по этому поводу, чтобы убедиться, что утечка памяти описанное вами не относится к их элементам управления и полученному окончательному решению.

person DmitryG    schedule 07.08.2014