2013-06-26 79 views
6

我有一個隨着時間的推移泄漏UserControls的CF應用程序。它花了一些時間,但我縮小了範圍,甚至在整個框架中複製了行爲(3.5)。既然這兩種行爲都存在,我不想把它稱爲一個錯誤,但我肯定不明白爲什麼會發生這種情況,並希望有人能夠對此有所瞭解。GC沒有最終化UserControl?

因此,我創建一個簡單的WinForms應用程序與一個窗體和按鈕。點擊按鈕會交替創建一個新的UserControl和處理該控件。很簡單。

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方法,簡單地將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,並且正在處理位圖等。 – 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