.NET Forum / .NET Framework / CLR / October 2006
Anonymous delegates and events
|
|
Thread rating:  |
ewolfman - 09 Oct 2006 23:36 GMT Hi,
I have a weird situation and I was wondering if anyone can help out.
In my WinForms application, I have several UserControls which contain other UserControls. Some of the "inner" UserControls raise events, which are handled by the "parent" UserControls. The event handling is performed using an anonymous event handler.
Using sos.dll, I discovered that the UserControls (parent and child) are not elligable for GC, when I'm disposing of the "parent" UserControl. I'm also making sure that the child's Dispose() is called, so please read on.
However, if I change the anonymous event handler to a "normal" event handler, sos.dll reports that the controls are elligable for collection, and indeed when I invoke GC.Collect (just for the debug purposes, of course) - they are collected ok.
I wasn't able to reproduce this on a test app.
The following (old) thread describes a similiar hypothetical situation, but there isn't a clear answer on this: http://groups.google.com/group/microsoft.public.dotnet.framework.clr/browse_frm/ thread/4583fa08a8cfc96a/974868e69bb0234a?lnk=st&q=anonymous+delegates+and+events &rnum=4#974868e69bb0234a
Thanks.
Chris Mullins - 10 Oct 2006 00:46 GMT I've tracked down a number of memory management issues that resovled to event handler hooks that weren't being cleaned up.
Anytime something has a hook into one of your events, you're entire object is considered still rooted and thus not eligible for GC. Being disposed or not disposed didn't really make any difference.
I know this doesn't quite answer your question, but just make sure to explicitly clear out all your event handlers. In VB this is easy - the 'withevents' stuff takes care of it for you. In C# it takes explicit code. Anytime you have a "+=" you need to have a "-=" as well. If not, you're going to be leaking memory.
If you're using SOS, it sounds like you have things under control, debugging wise. The tool I had the most success tracking this stuff down with was the SciTech memory profiler. You're already past the "tracking it down" phase though...
 Signature Chris Mullins, MCSD.NET, MCPD:Enterprise http://www.coversant.net/blogs/cmullins
> Hi, > [quoted text clipped - 22 lines] > > Thanks. ewolfman - 10 Oct 2006 00:56 GMT Hi Chris, thanks for your reply.
I forgot to mention, that *I am* using the '-=' operator. If you'll read my other "additions" post (posted AFTER you have posted your reply...), then you'll see my thoughts regarding anonymous delegates and removing the delegate from the EventHandlerList/Invocation List.
I've mentioned the Dispose, just in case someone would have replied 'use Dispose()'...
Once again, just to make sure: using the '-=' operator on a 'non-anonymous' event handler works great. It is the anonymous event handler which makes all the trouble. And again - in my "additions" post you can see that I wasn't able to reproduce this on a test application, using anonymous delegates. This is what's so weird about it.
Chris Mullins - 10 Oct 2006 17:33 GMT Is the class you're attaching an event to your own class?
If so, it would be interesting to have some trace code in the add/remove handlers for the events.
The only other thing I can think of is that you have some strange race condition that is, once-in-a-while, causing your code to remain rooted. This would explain the "cannot duplicate" part of the problem. What does the code in the anonymous mehtod look like?
 Signature Chris Mullins
> Hi Chris, thanks for your reply. > [quoted text clipped - 11 lines] > you can see that I wasn't able to reproduce this on a test application, > using anonymous delegates. This is what's so weird about it. ewolfman - 10 Oct 2006 00:49 GMT I have more to add to my original post.
When using an anonymous delegate as an event handler, and using the add/remove accessors to register the event on an EventHandlerList, I observe (what is expected) that the original anonymous event handler is NOT removed from the list.
This confirms what troubled me: using '-=' with an anonymous delegate, is worthless. If this is true, then using anonymous delegates as Event Handlers is a very risky business! They remain "forever" in the EventHandlerList/multicast-delegate's invocation list and do not allow GC on the referenced object (until the "child" UserControl is collected itself).
But what puzzles me still, is the fact that I could not reproduce this error. From this I gather that the "child" UserControl, in the "reproducing code" (which contains the EventHandlerList) has been cleared for GC, and therefore the "parent" has also been cleared for GC. This *is* the expected behaviour as far as I'm concerned. So back to my original post - why is my application behaving weird with anonymous Event Handlers?
Jon Shemitz - 10 Oct 2006 03:57 GMT > why is my application behaving weird with > anonymous Event Handlers? My guess is that sometimes you are explicitly saving the event handler, and sometimes you are not.
Code like
// subscribe EventDelegate Registered = new EventDelegate(validMethod); Event += Registered;
// cancel, as in IDispose.Dispose Event Event -= Registered;
will work with delegates to named methods. It's not necessary, though. You can just code
// subscribe Event += new EventDelegate(validMethod); // or "Event += validMethod", in 2
// cancel, as in IDispose.Dispose Event Event -= new EventDelegate(validMethod);
The -= doesn't care about delegate reference equality. It just matches instance reference and method reference. If (all) match, the delegate is removed. But with anonymous methods,
// subscribe Event += delegate { // whatever };
// cancel, as in IDispose.Dispose Event Event -= delegate { // it doesn't help if you replicate the code exactly! };
is pointing to different code. Does
// subscribe EventDelegate Registered = delegate { // whatever }; Event += Registered;
// cancel, as in IDispose.Dispose Event Event -= Registered;
work for you?
 Signature .NET 2.0 for Delphi Programmers www.midnightbeach.com/.net Delphi skills make .NET easy to learn Great reviews & good sales.
ewolfman - 10 Oct 2006 09:57 GMT Hi Jon, thanks for your reply.
> My guess is that sometimes you are explicitly saving the event > handler, and sometimes you are not. It's possible, althought I can't think of a way to tell if this is true.
> The -= doesn't care about delegate reference equality. It just matches > instance reference and method reference. If (all) match, the delegate > is removed. But with anonymous methods, True.
> // subscribe > Event += delegate [quoted text clipped - 7 lines] > // it doesn't help if you replicate the code exactly! > }; Correct again.
> is pointing to different code. Does > [quoted text clipped - 9 lines] > > work for you? No, it doesn't, as I've written earlier: "When using an anonymous delegate as an event handler, and using the add/remove accessors to register the event on an EventHandlerList, I observe (what is expected) that the original anonymous event handler is NOT removed from the list.
This confirms what troubled me: using '-=' with an anonymous delegate, is worthless". (Regarding to Jon Skeet's remark, this comment is "generalizing", so please note that I'm addressing a particular situation).
Please read my question, in my reply to Jon Skeet. I'll be happy if you have further comments.
Jon Skeet [C# MVP] - 10 Oct 2006 07:19 GMT > I have more to add to my original post. > [quoted text clipped - 9 lines] > GC on the referenced object (until the "child" UserControl is collected > itself). They're only useless in the situations where you *need* to remove event handlers. Most of the time when I write event handlers, the object "owning" the event becomes eligible for garbage collection at the same logical time as the target of the event handler, so it's not an issue.
 Signature Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet If replying to the group, please do not mail me too
ewolfman - 10 Oct 2006 09:15 GMT > They're only useless in the situations where you *need* to remove event > handlers. Most of the time when I write event handlers, the object > "owning" the event becomes eligible for garbage collection at the same > logical time as the target of the event handler, so it's not an issue. Correct. I was "generalizing".
I have a question: If both "parent" and "child" are not referenced by anyone, except for the child containing the reference to the anonymous delegate in the "parent", aren't they supposed to be eligible for GC?
This is what I expect it to be, and this is what my "reproducing code" behaves like. It's possible that I'm making a wrong assumption regarding my application, that when I replace the anonymous delegate to a non-anonymous delegate, they both become eligible for GC. But then again, it seems to be a fact.
Any ideas?
Ben Voigt - 10 Oct 2006 19:44 GMT >> They're only useless in the situations where you *need* to remove event >> handlers. Most of the time when I write event handlers, the object [quoted text clipped - 15 lines] > > Any ideas? If you are accurately describing the situation, that would be a bug. If object A registers a closure (anonymous delegate using "this" pointer) on object B's event, then object B has a reference to object A that you may not be able to get rid of easily. But that should not prevent the system "A+B" from being unreachable from roots and therefore collectable.
Free MagazinesGet 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 ...
|
|
|