Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / New Users / April 2008

Tip: Looking for answers? Try searching our database.

Form not disposing due to lingering reference

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
David Rosenkranz - 21 Apr 2008 14:11 GMT
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!
Peter Duniho - 21 Apr 2008 18:06 GMT
> i've got a problem with a Form, because it is not being disposed of when  
> I
[quoted text clipped - 3 lines]
> still "false".
> Obviously there must still be a reference to this Form instance.

If you are truly talking about _disposal_, then your assertion here is  
false.  Disposal of an object has absolutely zero to do with whether there  
are existing references to the object.  In fact, given that you can't call  
Dispose() explicitly on an object without a reference to the object, it  
should be obvious that the success of the Dispose() method doesn't depend  
on there being no references.

As far as your specific question goes, you should post a  
concise-but-complete code sample that reliably demonstrates the problem.  
In this case, that particularly means demonstrating why you need to check  
the "IsDisposed" property and why you care whether it's set to "true" or  
"false".  The mere fact that the Disposed event is being raised should be  
sufficient information for you, regarding whatever it is you're trying to  
do in that event.

If I had to guess, I'd guess that when the Disposed event is raised, the  
object is still in the process of being disposed.  The Disposing property  
is probably set to "true", while the "IsDisposed" property will probably  
remain "false" until the entire process of disposal has been completed  
(which would include raising the Disposed event).

But without a code example, it's impossible to say for sure what's going  
on in _your_ code, and it's impossible to offer specific advice as to how  
you can deal with the issue.

Pete
David Rosenkranz - 22 Apr 2008 09:10 GMT
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.
Mike Urquiola - 22 Apr 2008 16:38 GMT
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.

> Pete, thank you for your detailed response. From your words I guess I
> could
[quoted text clipped - 44 lines]
> Cheers
> David.
Peter Duniho - 22 Apr 2008 18:52 GMT
> Pete, thank you for your detailed response. From your words I guess I  
> could
> not make myself perfectly clear.

I think you've been clear enough.  But I'm not sure you fully understand  
memory management in .NET.

> Of course I know, that calling the Dispose method of an object does not  
> mean
[quoted text clipped - 11 lines]
> creating
> and holding.

Not that it necessarily matters here, but it's not the only reference that  
exists.  The Application class, for example, maintains a list of open  
forms.  As long as your form is open, it's referenced there as well.

> In OnMyFormDisposed I drop the reference
> m_myForm = null;

That's fine as far as it goes.  But you should not overstate what that  
does.

> I assumed, that now the form was disposed (what else should be the  
> reason to
> fire the Disposed event)

The form is being disposed when your Disposed event handler is called, yes.

> and all of my selfmade references to the form were dropped.

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.

> I concluded that now the GC should have the possibility to clean up
> and free resources.

That's too vague.  The disposal of the instance has already resulted in  
cleaning up and freeing of resources.  All that remains is for the memory  
to be collected.  And assuming the instance is no longer reachable from  
_any_ data structure, yes...the collector has the possibility to collect  
that memory.

> From this point I profiled the memory consumption of my application and  
> saw,
[quoted text clipped - 4 lines]
> System.Windows.Forms.Control/ControlNativeWindow" is still pointing at my
> object.

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 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.

Why do you make that assumption?  Unless something else is referring to  
those controls, those references would not prevent the collector from  
collecting the instance.  Do you have something else referring to those  
controls?  Have you ascertained for sure that those controls are in fact  
what's referencing the form instance?

> 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.

I don't know either.  Assuming this is a modeless form we're talking  
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.

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.

Pete
David Rosenkranz - 22 Apr 2008 19:37 GMT
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  
[quoted text clipped - 5 lines]
> 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 - 22 Apr 2008 20:08 GMT
> [...]
>> The form is being disposed when your Disposed event handler is called,  
[quoted text clipped - 5 lines]
> 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?

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 am using both YourKit for .NET and Memprofiler. Memprofiler actually  
[quoted text clipped - 4 lines]
> this reference is the only remaining rooted reference to the disposed  
> form.

A reference inside a ToolStrip instance isn't a rooted reference.  Unless  
something else refers to the ToolStrip instance, that reference doesn't  
make your form instance reachable, and so that reference wouldn't prevent  
the form from being collected.

Think of it this way: is your ToolStrip instance collectable?  If not,  
then neither is the form instance.  If it is, then so is the form instance.

You haven't answered the question until you know whether the ToolStrip  
instance is itself collectable.

> [...]
> You are most certainly right about the effect of the DIspose() method.  
[quoted text clipped - 3 lines]
> created
> and used only in my form class.

If that's true, then it's not reachable and thus isn't preventing your  
form from being collected.

So either your assertion about collection is wrong, or your assertion  
about the references to the ToolStrip instance is wrong.

Pete
David Rosenkranz - 23 Apr 2008 15:12 GMT
> 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  
[quoted text clipped - 4 lines]
> 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!
Peter Duniho - 23 Apr 2008 17:50 GMT
> [...] When I do this on my
> machine, my profiler shows, that this ToolStripForm is not garbage  
> collected
> in a reasonable timespan.

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.

> 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
[quoted text clipped - 5 lines]
> and
> http://www.david-rosenkranz.de/memprofiler.png

Not being familiar with either tool, I can't say that the screenshots help  
me that much.  Maybe someone else will have better insight.  The first  
screenshot seems simpler to understand, and I gather that each line in the  
lower pane represents a path that the profiler _believes_ to be a rooted  
path to your instance.  But I don't know enough about the profiler to know  
whether those are reliable indicators.  Nor did you expand any of the  
multi-node paths to show what the actual root was.

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

Well, I don't have an answer for that question.  There are two  
possibilities: either this is a genuine issue that no one has ever noticed  
before; or your tools are misleading you.

Absent any other information, I'd give each of those possibilities a  
straight 50/50 chance of being correct.  YMMV.  :)

Pete
David Rosenkranz - 24 Apr 2008 13:05 GMT
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.
Peter Duniho - 24 Apr 2008 18:15 GMT
> [...]
> 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?

Not for me, sorry.  If I take that output at face value, all it's telling  
me is that the ToolStrip instances are being considered "rooted" for the  
same reason that your form is (i.e. somewhere there's an instance of the  
"Control/ControlNativeWindow" type that has a member "control" that leads  
to the actual Form and ToolStrip instances).

Presumably, if the form could be unrooted, so too would the ToolStrip  
instances.  All of this, of course, assumes that the memory profiler is  
providing accurate information and I'm interpreting it correctly.  Neither  
of these are actually a given.  Since it's all I have to go on, I'm afraid  
I don't have anything more useful to add.

You really will need to find out why the "control" member is being  
considered a root by your memory profiler.  I'd say one place to start is,  
"why does the type Control/ControlNativeWindow have a slash in it?" and  
"who is actually storing the instance of that type?"

Pete
David Rosenkranz - 25 Apr 2008 11:15 GMT
..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-toolstriptextboxc
ontrol/


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.

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.