0

I have 2 windows forms frmMain & frmDialog. When I click a button on frmMain, frmDialog opens. Here is my code for the click event of the button:

frmDialog f2 = new frmDialog();

f2.Show();

When I keep on clicking the button, new forms appear & don't close. An object gets garbage collected when it goes out of scope.

My question is:

Why doesn't variable f2 get garbage collected when it goes out of scope?

Is it a memory leak?

Laurence
  • 721
  • 7
  • 24
Moiz Syed
  • 57
  • 4
  • Do you ever close the form ? Your question does not make it clear. Whay shouldn't "new frmDialog()" not create a new Form instance ? – PhillipH Apr 25 '17 at 06:44
  • 2
    Garbage collection has nothing to do with showing windows. Also, "An object gets garbage collected when it goes out of scope" is incorrect. A closer to correct statement is that "an object *becomes eligible for collection* when it can be demonstrated that no further use of that object will occur". This can be *earlier* than the end of a "scope" and also just states *eligibility*. There's no guarantee on *when* the object will be collected. – Damien_The_Unbeliever Apr 25 '17 at 06:44
  • @Smartis - only so long as "you", whatever you mean by that word, isn't *also* eligible for collection. – Damien_The_Unbeliever Apr 25 '17 at 06:45
  • When I close the frmMain, frmMain as well as all dialogs close – Moiz Syed Apr 25 '17 at 06:47

2 Answers2

1

There is a trick. When you call Show, the property Visible is set to true, which in turn calls SetVisibleCore: http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Form.cs,b31b076f655b0b4b

The interesting bits are:

if (CalledOnLoad) {
    // VSWhidbey 518085: make sure the form is in the Application.OpenForms collection
    if (!Application.OpenFormsInternal.Contains(this)) {
        Application.OpenFormsInternalAdd(this);
    }
}

The form is added to a collection stored in a static property, Application.OpenFormsInternal. Therefore, your form gets rooted and won't be eligible to garbage collection until closed (and automatically removed from that collection).


Following Hans Passant's comment, I played a bit with a memory dump:

0:000> !gcroot 000000000263d368
HandleTable:
0000000000191348 (strong handle)
-> 000000000263d368 WindowsFormsApp1.Form1

00000000001917d0 (pinned handle)
-> 00000000125f99a8 System.Object[]
-> 000000000262c9a8 System.Windows.Forms.FormCollection
-> 000000000262c9d8 System.Collections.ArrayList
-> 000000000262ca00 System.Object[]
-> 000000000263d368 WindowsFormsApp1.Form1

We see the Application.OpenFormsInternal collection (the second root), but also a strong handle that would be keeping the form alive even if it wasn't present in the collection (which apparently could happen).

Community
  • 1
  • 1
Kevin Gosse
  • 38,392
  • 3
  • 78
  • 94
  • 1
    Looks convincing, but [it is not correct](http://stackoverflow.com/a/3751748/17034). The reference is kept at a much lower level. Look at NativeWindow.CreateHandle(), you'll find the HandleCollector class. – Hans Passant Apr 25 '17 at 07:41
  • @HansPassant Oh. I stopped at the first root, I guess I should have dug deeper. – Kevin Gosse Apr 25 '17 at 16:02
0

Yes, it is memory leak.

In C# you have something like two kinds of memory. Managed memory and unmanaged memory. Managed memory is managed by GC :) Unmanaged is not. You have to free unmanaged memory by yourself. So for example (managed):

{
    List<int> l = new List<int(); 
}

This one won't create memory leak. After l goes out of scope it is marked to be freed. It is marked not freed. It may be freed later (list destructor will be called then).

So how about unmanaged memory?

{
    MyForm f = new MyForm();
}

This is memory leak. When f goes out of scope, its unmanaged memory is not managed by GC at all. So you have to free it by yourself:

{
    MyForm f = new MyForm();
    f.ShowDialog();
    f.Dispose();
}

Dispose method frees all unamanaged memory. So now you may have two questions: 1. How do I know if a class is managed or unmanaged? It's pretty simple. You can assume that all classes that implement IDisposable interface (contain Dispose() method) are unmanaged - they are unmanaged by themselves or they consist of unmanaged classes. Especially every resources like fonts, streams, forms, database connections and so on.

  1. How should I free modeless form? You could store a reference to that form and dispose it when it's no longer necessary. You could call Dispose in FormClosed event ( https://msdn.microsoft.com/en-us/library/system.windows.forms.form.formclosed.aspx )
Adam Jachocki
  • 1,897
  • 1
  • 12
  • 28