2013-06-26 4 views
6

У меня есть приложение CF, которое со временем течет UserControls. Это заняло некоторое время, но я сузил его и даже воспроизвел поведение в полной структуре (3.5). Поскольку поведение существует в обоих случаях, я не хочу называть его ошибкой, но я уверен, что не понимаю, почему это происходит, и надеюсь, что кто-то может пролить свет на него.GC не завершает работу с UserControl?

Поэтому я создаю простое приложение WinForms с формой и кнопкой. Нажатие на кнопку чередуется между созданием нового UserControl и Disposing этого элемента управления. Очень просто.

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
    } 

    UserControl1 m_ctl; 

    private void button1_Click(object sender, EventArgs e) 
    { 
     if (m_ctl == null) 
     { 
      m_ctl = new UserControl1(); 
      m_ctl.Visible = true; 
      this.Controls.Add(m_ctl); 
     } 
     else 
     { 
      this.Controls.Remove(m_ctl); 
      m_ctl.Dispose(); 
      m_ctl = null; 
      GC.Collect(); 
     } 
    } 
} 

И вот UserControl. Он просто отслеживает количество живых (то есть не завершенных) экземпляров. У него ничего нет, кроме единственной метки, чтобы я мог визуально подтвердить, что она находится в Форме.

public partial class UserControl1 : UserControl 
{ 
    private static int m_instanceCount = 0; 

    public UserControl1() 
    { 
     var c = Interlocked.Increment(ref m_instanceCount); 
     Debug.WriteLine("Instances: " + c.ToString()); 

     InitializeComponent(); 
    } 

    ~UserControl1() 
    { 
     var c = Interlocked.Decrement(ref m_instanceCount); 
     Debug.WriteLine("Instances: " + c.ToString()); 
    } 
} 

Странная вещь здесь заключается в том, что количество экземпляров растет бесконечно. В конце концов, на устройстве у меня заканчивается память. Я подозреваю, что я тоже на ПК, я просто не склонен нажимать кнопку на следующий год.

Теперь, если я изменить по умолчанию, конструктор сгенерированный Dispose метод в UserControl как это, просто добавив вызов ReRegisterForFinalize:

protected override void Dispose(bool disposing) 
{ 
    if (disposing && (components != null)) 
    { 
     components.Dispose(); 
    } 

    base.Dispose(disposing); 

    if (disposing) 
    { 
     GC.ReRegisterForFinalize(this); 
    } 
} 

Затем он ведет себя точно так, как и ожидалось, Доработка экземпляров во время сбора (при ручной или автоматический) ,

Так почему же это происходит? Очевидно, что база вызывает SuppressFinalize, но почему именно это происходит, и почему во имя Одина это поведение по умолчанию?

+0

+1 на самом деле имея реальную причину для рассмотрения с помощью GC, – Sayse

+0

Учитывая, Риду (жаль не может помочь на фактическом решении!) ответ, что делает финализатор в вашем реальном коде? Не мог ли вызвать финализатор вызвать утечку? – svick

+0

Реальный код вообще не имеет финализаторов. У них есть Dispose, и Bitmaps и т.п. удаляются. – ctacke

ответ

4

Так почему же это происходит? Очевидно, что база вызывает SuppressFinalize, но почему именно это происходит, и почему во имя Одина это поведение по умолчанию?

Это поведение по умолчанию для классов (правильно), реализующих IDisposable. Когда вы вызываете IDisposable.Dispose, предлагаемое поведение по умолчанию - это подавление финализации, поскольку основной причиной завершения является очистка ресурсов , которые никогда не были удалены. Это связано с тем, что финализация является дорогостоящей операцией - вы не хотите окончательно завершить объекты без необходимости, и если было вызвано Dispose, мысль состоит в том, что вы уже очистили свои не управляемые ресурсы. Любая управляемая память будет обработана независимо.

Вы должны переопределить Dispose и выполнить свой декремент в пределах Dispose переопределения.

Эта проблема объясняется в documentation for IDisposable. Образец реализация вызова Dispose метода (от справочной документации):

public void Dispose() 
{ 
    Dispose(true); 
    // This object will be cleaned up by the Dispose method. 
    // Therefore, you should call GC.SupressFinalize to 
    // take this object off the finalization queue 
    // and prevent finalization code for this object 
    // from executing a second time 
    GC.SuppressFinalize(this); 
} 
+0

Единственная проблема заключается в том, что в реальной среде приложения, когда вы перемещаетесь по приложению, происходит сканирование памяти, и GC никогда не восстанавливает эту память. К счастью, все мои представления получены из единого базового UserControl. Когда я повторно регенерирую для этой базы, утечка исчезнет. Ни один из моих представлений не реализовал финализаторы. По ассоциации это, кажется, является причиной проблемы. – ctacke

+0

Что изменило мне этот путь, было использование .NET Memory Profiler, в котором говорилось, что есть мнения, которые были «расположены, но не доработаны». – ctacke

Смежные вопросы