Reputation: 1284
Could someone please tell me what is going wrong?
After removing tons of code to find our GDI object leak (using task manager and watching the "GDI Objects" column grow to 10,000 and our app crashing) I reduced the code down to only .net code without any custom business code. We are still getting the issue.
I created a test app to replicate the issue, which has the following basic behavior.
Now the form that is being launched 150 times is setup in a specific way (Lets call the form "BadForm". The is a static datatable the is bound to a combobox on the form.
BadForm has a comboBox on it, and a timer. Here is the code for the form:
using System;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
namespace GDIObjectLeakTest
{
public partial class MyForm :Form
{
public static DataTable CachedNodeType = new DataTable();
public MyForm()
{
InitializeComponent();
this.comboBox1.SelectedIndexChanged += new EventHandler(this.comboBox1_SelectedIndexChanged);
this.Font = new Font("Modern No. 20", 8.249999F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0))); ;
comboBox1.DataSource = CachedNodeType;
}
private void timer1_Tick(object sender, EventArgs e)
{
Close();
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{ }
}
}
Here is the code for the main form of the app that runs the test. It has 2 buttons on it. Button1 runs the BadForm 150 times. Button 2 runs the garbage collector 100 times (Once or twice isn't good enough for me I guess) (I'm using the Garbage Collector just to proove there is/isn't a problem).
private void button1_Click(object sender, EventArgs e)
{
try
{
for(int i = 0; i < 150; i++)
{
//new SearchForm().Show();
new MyForm().Show();
}
} catch(Exception ee)
{
throw;
}
}
private void button2_Click(object sender, EventArgs e)
{
for(int i = 0; i < 100; i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
Upvotes: 1
Views: 2323
Reputation: 153
Try adding this to the top of your dispose method (in the designer file):
comboBox1.DataSource = null;
Upvotes: 2
Reputation: 1250
You are using Font object which is an GDI object and not disposed until you dispose it. Either use Font object with using statement, or call Font.Dispose on FormClose event.
Upvotes: 0
Reputation: 2398
I see two potential issues here:
If you create an instance of an object that implements IDisposable
, you must call the Dispose method on it, or wrap its use in a using
block. Form implements IDisposable, so your code should look like the following:
using (Form myform = new Form())
{
myform.Show();
} //frees resource by calling Dispose automatically
otherwise, you will see the memory leaks you see here, because you are creating new instances of your Form, but then you are never freeing its resources. Garbage collection might eventually free up Windows resources for you in WinForms, due to the way that finalizers are written in the BCL, but calling Dispose will do it immediately.
You are creating a new Font
object and assigning it to the Font property each time your form is initialized. Not that this is necessarily bad (designer generated code does this copiously), but each new Font
instance occupies a GDI handle, which will not be freed automatically unless you call Font.Dispose. My guess is that you are leaving another Font object behind that might not be getting Disposed properly. You may want to optimize this in some way (such as by sharing a Font instance) if you are making huge numbers of these objects. In at least one case, not calling Dispose on fonts will cause this memory leak.
Upvotes: 0