Mam aplikację CF, która z czasem wycieka UserControls. Zajęło to trochę czasu, ale zawęziłem go, a nawet replikowałem zachowanie w pełnym kontekście (3.5). Ponieważ zachowanie istnieje w obu, nie chcę tego nazwać błędem, ale na pewno nie rozumiem, dlaczego tak się dzieje i mam nadzieję, że ktoś może rzucić trochę światła na to.GC nie finalizuje UserControl?
Tworzę więc prostą aplikację WinForms z formularzem i przyciskiem. Kliknięcie przycisku przełącza się między utworzeniem nowego kontrolera UserControl a usunięciem tego elementu sterującego. Bardzo prosta.
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();
}
}
}
I tutaj jest UserControl. Po prostu śledzi liczbę aktywnych (tzn. Nie sfinalizowanych) wystąpień. Nie ma w nim nic poza pojedynczą etykietą, więc mogę wizualnie potwierdzić, że jest w Formie.
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());
}
}
Dziwne jest to, że liczba instancji rośnie w nieskończoność. W końcu na urządzeniu zabrakło mi pamięci. Podejrzewam, że również na PC, nie jestem skłonny do klikania przycisku na następny rok.
Teraz gdybym zmienić domyślną, projektant generowane Dispose metody usercontrol w ten sposób, po prostu dodając wywołanie ReRegisterForFinalize:
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
if (disposing)
{
GC.ReRegisterForFinalize(this);
}
}
Wtedy zachowuje się dokładnie tak, jak oczekiwano, Finalizacja wystąpień podczas odbioru (gdy ręczne lub automatyczne) .
Dlaczego tak się dzieje? Najwyraźniej baza nazywa SuppressFinalize, ale dlaczego tak się dzieje i dlaczego w imieniu Odina jest to domyślne zachowanie?
+1 do rzeczywiście ma prawdziwy powód do rozważenia przy użyciu GC – Sayse
Zważywszy Reeda (przepraszam nie może być pomocne na rzeczywiste rozwiązanie!) odpowiedź, co robi finalizator w twoim prawdziwym kodzie? Czy wywołanie finalizatora nie mogło spowodować wycieku? – svick
Rzeczywisty kod nie ma żadnych finalizatorów. Posiadają Dispose wdrożone, a bitmapy i podobne są usuwane. – ctacke