dotnet clr:
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.
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... -- Chris Mullins, MCSD.NET, MCPD:Enterprise http://www.coversant.net/blogs/cmullins [quoted text, click to view] "ewolfman" <ewolfman@yahoo.com> wrote in message news:1160433394.946617.29020@i42g2000cwa.googlegroups.com... > 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. >
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?
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.
[quoted text, click to view] ewolfman wrote: > 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? -- ..NET 2.0 for Delphi Programmers www.midnightbeach.com/.net
[quoted text, click to view] ewolfman <ewolfman@yahoo.com> wrote: > 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).
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. -- Jon Skeet - <skeet@pobox.com> http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
[quoted text, click to view] > 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?
Hi Jon, thanks for your reply. [quoted text, click to view] > 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. [quoted text, click to view] > 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. [quoted text, click to view] > // subscribe > Event += delegate > { > // whatever > }; > > // cancel, as in IDispose.Dispose > Event Event -= delegate > { > // it doesn't help if you replicate the code exactly! > };
Correct again. [quoted text, click to view] > > is pointing to different code. Does > > // subscribe > EventDelegate Registered = delegate > { > // whatever > }; > Event += Registered; > > // cancel, as in IDispose.Dispose > Event Event -= Registered; > > 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.
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? -- Chris Mullins [quoted text, click to view] "ewolfman" <ewolfman@yahoo.com> wrote in message news:1160438172.710308.175140@i3g2000cwc.googlegroups.com... > 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. >
[quoted text, click to view] "ewolfman" <ewolfman@yahoo.com> wrote in message news:1160468152.686078.120900@h48g2000cwc.googlegroups.com... >> 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? >
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.
Don't see what you're looking for? Try a search.
|