Form not disposing due to lingering reference

  • Thread starter Thread starter David Rosenkranz
  • Start date Start date
D

David Rosenkranz

Hi everyone,

i've got a problem with a Form, because it is not being disposed of when I
close the form. Nevertheless, callback methods registerd to the
"Form.Disposed" event of this very Form are being called. But when the
callback method are being executed the "Form.IsDisposed" property is still
"false".
Obviously there must still be a reference to this Form instance. But this
reference is not explicitly created or managed by my code. I analysed my app
with the YourKit Profiler for .NET and found out, that the "control" member
of a "System.Windows.Forms.Control/ControlNativeWindow" instance is
referencing my Form.
Can anyone please give me a clue on
(a) what this ControlNativeWindow is
(b) how / when / why it is created
and most important (c) how I can get rid of this reference to my Form.

Any input welcome!

Thank you!
 
Pete, thank you for your detailed response. From your words I guess I could
not make myself perfectly clear.
Of course I know, that calling the Dispose method of an object does not mean
killing references to this object. So, what am I doing:

In the initialization method of one of my classes the following assertion to
a member variable (MyForm m_form) is made:

m_myForm = new MyForm();
m_myForm.Disposed += new EventHandler(OnMyFormDisposed);
m_myForm.Show();

m_myForm is the only reference to the MyForm instance that _I_ am creating
and holding.

In OnMyFormDisposed I drop the reference
m_myForm = null;

I assumed, that now the form was disposed (what else should be the reason to
fire the Disposed event) and all of my selfmade references to the form were
dropped. I concluded that now the GC should have the possibility to clean up
and free resources.

From this point I profiled the memory consumption of my application and saw,
that the GC never took care of MyForm instances. I looked at the GC paths and
realized that there was still at least one reference to the object. My
profiler says that "control of
System.Windows.Forms.Control/ControlNativeWindow" is still pointing at my
object. I assume that this has something to do with the controls which I
place on the form: a ToolStrip with some ToolStripButtons and a TabControl
containing a ListView.

With trial-and-error I meanwhile discovered that the form is only garbage
collected if I call this.Dispose() inside a Form.Closing event handler AND I
set the AllowItemReorder property of the contained ToolStrip to false. Why
does this work? Frankly, I don't know.

Cheers
David.
 
My understanding is that .Dispose() does not get called automatically by the
GC. Maybe you are confusing it with a Finalizer?
The IDispose.Dispose() interface is supposed to get rid of any unmananged
resources and references that could unitentionally keep objects alive (event
handlers etc). Some framework components I've noticed will when they are
done with an object call .Dispose() if it implements this interface, but in
general you have to call it yourself.

In your case the toolstrip probably has a reference to the root for
responding to an event of somekind, setting the property likely removes the
event also the toolstrip.dispose() should get rid of that. I would first
make sure that it is added to the Form.Controls collection, I've noticed
with some 3rd party controls that if it is not added then it never gets
cleaned up. Another approach if this doesn't work is to GetAllControls on
form close and call dispose on the ones that implement IDispose.

..Net Memory Profiler is wonderful for finding these kinds of leaks. Not
sure if that is what you're using.
 
Thanks again. I think this discussion really helps me a lot!
I think you've been clear enough. But I'm not sure you fully understand
memory management in .NET.

Well, you could say it this way :-)
The form is being disposed when your Disposed event handler is called, yes.

What does it mean exactly when the Disposed event is fired? Is the form
being disposed or has it been disposed. In case of the latter (which I would
prefer because of the event's name) why is the value of the IsDisposed
property of my form still false when I read it inside the event handler?
You don't have control over all possible references. However, yes...I
would expect generally that the instance will eventually be collected,
assuming it's no longer reachable.
What profiler are you using? Are you sure it's correct? If it is, note
that having a reference to your form is a necessary, but not sufficient,
condition to prevent collection.

I am using both YourKit for .NET and Memprofiler. Memprofiler actually gave
me a new clue. It says, my form is being referenced by an event handler
connected to the ToolStrip instance I mentioned. And according to my profiler
this reference is the only remaining rooted reference to the disposed form.
I don't know either. Assuming this is a modeless form we're talking

Yes, it is.
about, I doubt that calling Dispose() has any effect at all (closing a
modeless form causes it to be disposed). As long as the ToolStrip in the
form isn't being referred to anywhere else in your code, it having a
reference to the form shouldn't have any effect.

You are most certainly right about the effect of the DIspose() method. But
in my profilers I can see the effect, nevertheless.
The ToolStrip isn't referenced anywhere else in my code. It is being created
and used only in my form class.
I suppose there's a possibility that the AllowItemReorder property, when
set to true, causes your ToolStrip item to be referenced by something
else. But then presumably that would show up in your memory profiling.

Again: an instance being referenced does not in and of itself prevent the
instance from being collected. The object must be reachable. If you
think your form instance isn't being collected, you need to follow the
references all the way back to some "rooted" reference that causes the
instance to be reachable.

Maybe it could help if I post or link to a screenshot of my profiler showing
the rooted reference to my form?

Thank you!
 
Peter Duniho said:
I don't know the precise answer to the question. However, assuming that
IsDisposed is false in your handler, I take that as a clear indication
that your handler is called during disposal, not after. What does the
Disposing property return? I suspect "true".

Still, I don't see how it matters. You haven't described any code in
which it matters whether disposal has been completed when your handler is
called.

I read the IsDisposed property just for curiosity. Just to find out wether
the event handler is called _after_ or _during_ the disposal process.

To demonstrate what I mean, I just created a simple VS2008 project which you
can find on my website at http://www.david-rosenkranz.de/ToolStripper.zip
You may compile it yourself or just start the ToolStripper.exe from the
bin/debug directory and connect it to your favorite profiler.
The "application"'s main form just has a button. When you click it, second
form (instance of ToolStripForm) shows up, containing nothing more than a
ToolStrip with a handful of buttons. If you do nothing with this second form
and just close it, you will see in the profiler, that the ToolStripForm
instance will be garbage collected a few seconds after closing it. Fine so
far.
But, now click the button again to show a new instance of ToolStripForm.
Now, before closing it, just click on the ToolStripOverflowButton at the
bottom right of the ToolStrip to show the hidden buttons, which do not have
enough space in the bar. Click the ToolStripOverflowButton again, to hide the
additional buttons and then close the ToolStripForm. When I do this on my
machine, my profiler shows, that this ToolStripForm is not garbage collected
in a reasonable timespan. Take a memory snapshot and you will see that there
still IS a root path to the form. The path from root to the form also
contains the ToolStrip instance of this form.
In case you don't see the effect or don't want to start/profile my project
I've also put two screenshots onto my website, one from each profiler:
http://www.david-rosenkranz.de/yourkit.png
and
http://www.david-rosenkranz.de/memprofiler.png

In one of my earlier posts I said, that I experimented with the
AllowItemReorder property of the ToolStrip. In my small demo project, I have
this property set to false (default). If you set it "true" in the Form
Designer of VS and then start the application the ToolStripForm instances
will never be garbage collected, no matter if you pressed the overflow button
or not.

I think this behavior is quite strange, and I truly wonder, why I could not
find any report on this on the web...

Thank you!
 
Hi Pete (and everyone else)
Not that this necessarily explains your issue, but there is no such thing
as "a reasonable timespan". The garbage collection will happen when it
happens. It could be seconds, minutes, or even hours, depending on what
else is happening.

Of course, by definition, there is no "reasonable timespan" nor is there a
predictable result of a GC run. My assumptions are all based on experience
after watching in my profiler how the GC usually works when executing my app.
I think it would be most unlikely, that in the good cases the GC operates
quite fast and in all the bad cases (overflow button clicked or
AllowItemReorder==true) it doesn't work for hours. Possible - for sure - but
unlikely.
whether those are reliable indicators. Nor did you expand any of the
multi-node paths to show what the actual root was.

At http://david-rosenkranz.de/toolstripper.html you find a completely
expanded version of the rooted references tree to my form object. Maybe this
helps a little?

Cheers!
David.
 
...I just found an article which helped me to solve the problem. It seems,
that there is a problem with the ToolStrip-Elements in the .NET framework. If
a toolstrip has certain properties (eg. AllowItemReorder==true) or the user
does certain actions (eg clicking on the overflow button, or hiding/showing
the toolstrip) the ToolStrip implementation creates and registers event
handlers in a static event handler storage. Because static objects are GC
roots, the toolstrip and the surrounding form is now rooted and therfore
cannot be collected by GC. So you have to break that root path somehow. Best
would be to deregister the event handlers.
The article I found describes how to do this using ToolStripTextBox as an
example
http://www.scitech.se/blog/index.php/2007/10/05/memory-leak-in-toolstriptextboxcontrol/

Following the steps described in the article I discovered, that my problem
is caused by a UserPreferenceChangedEventHandler which is being registered by
the ToolStrip-Implementation when it shows a dropdown as the result of a
click on the ToolStripOverflowButton. The dropdown actually is a Toolstrip
itself, and is therefore a Control with a ControlNativeWindow. When the
control is created, a handle is created to. And on creation of the handle,
the event handler is being registered. So the solution to my problem would be
to destroy the handle, because that would deregister the eventhandler. I
don't know why this is not be done automatically when the dropdown is closed
or at least when the surrounding form is being disposed. So I do it myself:
in the the Dispose method of my form implementation (found in
MyForm.Designer.cs) I simply call

this.myBadToolstrip.OverflowButton.DropDown.Dispose();

This disposes the toolstrip dropdown, which implicitly destroys the
mentioned handle, which again deregisters the problematic event handler.

The problem which I could not solve up to now is that AllowItemReorder=true
also seems to register event handlers in a static registration. And I still
cannot see how to get rid of this. Simply setting
this.myBadToolstrip.AllowItemReorder=false in the Dispose method of my form
does not to the job.

I hope this might help somebody...

Cheers.
 
Back
Top