Winforms garbage collection and finalizers...what am I missing?

R

Robert Conde

Consider the test app below. While I hit the breakpoint in the finalizer in
DataClass, I never hit the one in Form2. How is that possible? Won't Form2
hold a reference to DataClass until it's finalized, therefore preventing
finalization of DataClass? Or can GC say "no one has a reference to Form2,
and Form2 is ready for finalization, so we can finalize DataClass and ignore
Form2 for now because it's small". I could maybe understand that - but I
NEVER hit the breakpoint in the Form2 finalizer, even on application
shutdown. Kinda makes it hard to know if I have dangling references.

using System;
using System.Windows.Forms;

namespace FinalizerTestWinforms
{
static class Test
{
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
}

class Form1 : System.Windows.Forms.Form
{
Timer timer = new Timer();

public Form1()
{
timer.Tick += timer_Tick;
timer.Start();
}

void timer_Tick(object sender, EventArgs e)
{
Form2 newForm = new Form2();
newForm.Show();
newForm.Dispose();
newForm = null;

GC.Collect();
}
}

class Form2 : System.Windows.Forms.Form
{
DataClass data = new DataClass();

~Form2()
{
int five = 5;//put breakpoint here
}
}

class DataClass
{
double[] data = new double[5000000];//to add memory pressure...

~DataClass()
{
int five = 5;//put breakpoint here...
}
}
}
 
S

Sheva

Since you call the Dispose() method on Form2 instance, so there is no
surprise of what you've experienced here.
because when you call Dispose() method, you will actually end up calling
System.ComponentModel.Component's Dispose() method, and in this method, you
will see something like the following:
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
So when you call Dispose() method on Form2, it will turn out that you
actually prevent Form's Finalizer from being called by the GC.Sheva
 
S

Stoitcho Goutsev \(100\)

Robert,

The purpose of the finalized is to dispose the form if it hasn't been
disposed yet. You dispose explicitely your form, though. Having the
finalizer called will only make the form object heavier for GC, so the
control's (actually this comes from the Component class) finalizers
correctly remove itself from the collection of objects that needs to be
finalized, thus the finalizer is indeed not called.
 
R

Robert Conde

Thanks guys - this clears it up for me.

Rob

Stoitcho Goutsev (100) said:
Robert,

The purpose of the finalized is to dispose the form if it hasn't been
disposed yet. You dispose explicitely your form, though. Having the
finalizer called will only make the form object heavier for GC, so the
control's (actually this comes from the Component class) finalizers
correctly remove itself from the collection of objects that needs to be
finalized, thus the finalizer is indeed not called.


--
HTH
Stoitcho Goutsev (100)

Robert Conde said:
Consider the test app below. While I hit the breakpoint in the finalizer
in DataClass, I never hit the one in Form2. How is that possible? Won't
Form2 hold a reference to DataClass until it's finalized, therefore
preventing finalization of DataClass? Or can GC say "no one has a
reference to Form2, and Form2 is ready for finalization, so we can
finalize DataClass and ignore Form2 for now because it's small". I could
maybe understand that - but I NEVER hit the breakpoint in the Form2
finalizer, even on application shutdown. Kinda makes it hard to know if I
have dangling references.

using System;
using System.Windows.Forms;

namespace FinalizerTestWinforms
{
static class Test
{
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
}

class Form1 : System.Windows.Forms.Form
{
Timer timer = new Timer();

public Form1()
{
timer.Tick += timer_Tick;
timer.Start();
}

void timer_Tick(object sender, EventArgs e)
{
Form2 newForm = new Form2();
newForm.Show();
newForm.Dispose();
newForm = null;

GC.Collect();
}
}

class Form2 : System.Windows.Forms.Form
{
DataClass data = new DataClass();

~Form2()
{
int five = 5;//put breakpoint here
}
}

class DataClass
{
double[] data = new double[5000000];//to add memory pressure...

~DataClass()
{
int five = 5;//put breakpoint here...
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top