Would subscribing to my own C# event create a memory leak? - c#

If a base class publishes a C# event and a derived class subscribes to it -- i.e. subscribes to itself --. Will the event subscription prevent the object from being garbage collected? Or is the garbage collector smart enough to detect such circular reference situations.
At first glance, it seems like it should but I'm pretty sure I've seen control code that does this. This is such a fundamental question I can't believe I've never looked into it before.
Edit: For Juan R. I mean something like this. (Never compiled this code, just typed it off the top of my head so I might have typos/errors)
public class Base
{
public event EventHandler<double> ValueChanged;
}
public class Derived : Base
{
public Derived()
{
// Will this prevent my object from being collected?
ValueChanged += OnValueChanged;
}
private void OnValueChanged(object sender, double v)
{
}
}

An object subscribing to its own event will not cause a memory leak for the simple reason that the CLR GC is based on reachability, not reference counting. If the object is reachable from a GC root, then it was not eligible for GC anyway. And if it's not reachable, then a self-reference does not make it reachable. There is nothing special about events with regard to GCing circular references.

Related

Garbage cleanup on c# events [duplicate]

This question already has answers here:
Is it necessary to explicitly remove event handlers in C#
(2 answers)
Closed 5 years ago.
Let's say I have a c# class which can be subscribed to like so:
public class Emitter {
public delegate void EmitAction(Emitter emitter);
public event EmitAction OnEmitted;
public void DoEmit() {
if(OnEmitted != null)
OnEmitted(this);
}
}
...and another class that makes use of it:
public class EmissionUser {
public void SomeFunction() {
Emitter em = new Emitter();
em.OnEmitted += HandleIt;
em.DoEmit();
}
public void HandleIt(Emitter em) {
//I could just do em -= HandleIt, but is there any point?
}
}
I know that for code cleanliness and avoiding memory leaks, it's always good have one unsubscribe for every subscribe when using events. However in a case like this, the Emitter instance is created locally in SomeFunction(), and not held in memory for too long, so I'm guessing the event subscription is destroyed with the local instance.
Is it still a good idea to unsubscribe in the handler? If so why?
Well, em is a local instance and will get destroyed with the end of HandleIt. You can choose to leave it like that if you are not going to use it anywhere else.
But if you have the reference of it somewhere else, or going to create one in future, it would create problems. Also, finding root cause of bugs related to events is tricky.
I noticed that you are passing the emitter reference. Someone can again use the same reference to call different method or raise an event within handleIt.
So, I would suggest you to do it, just for the sake of future-proofing the code.
Yes you don't need to unsubscribe because as you said the em variable destroyed after the SomeFuction returned, and nobody else has reference to this object so the garbage collector can clean it up.

Garbage collection of unreferenced object with live event handler

In the following program...
using System;
class Program
{
static Parent parent;
static void Main( string[] args )
{
parent = new Parent();
// The program hereafter runs for a long time and occasionally
// causes parent.SomeEvent to be raised.
}
}
class Parent
{
public event EventHandler SomeEvent;
public Parent()
{
new Handler( this );
}
}
class Handler
{
public Handler( Parent parent )
{
parent.SomeEvent += parent_SomeEvent;
}
void parent_SomeEvent( object sender, EventArgs e )
{
// Does something important here.
}
}
Notice that the instantiated Handler object is not referenced, although it has subscribed to SomeEvent. Is it possible that, after the program is running for a while, the garbage collector may decide to eliminate the Handler instance and its parent_SomeEvent handler will therefore no longer be called whenever parent.SomeEvent is raised?
I need this clarification for an app I am writing. There are many Handler-like objects that are instantiated as shown above, without being referenced. The central purpose of Handler is to subscribe to SomeEvent. There are no useful methods to call on a reference to the Handler instance, so I would be fine not referencing it otherwise. I haven't encountered any problems while debugging. But now I am concerned that issues may arise after deployment when the app is running for long periods of time and the garbage collector is more active.
The instantiated object of your Handler class will not be garbage collected until all references to that object are removed.
Thus, until you unsubscribe all your event handlers, the object will live. Because the subscribed event handler is also another "reference" that connects the Parent instance to the Handler instance.
Is it possible that, after the program is running for a while, the
garbage collector may decide to eliminate the Handler instance and its
parent_SomeEvent handler will therefore no longer be called whenever
parent.SomeEvent is raised?
That's exactly the reason why the GC only collects "unreferenced" objects from the heap. Your scenario would result in undefined NullReferenceExceptions, completely dependent on whenever the GC decides to delete objects. So luckily that's not the case :).
Furthermore, the GC is intelligent enough to determine isolated pools of unreferenced objects (unreferenced islands). So in the scenario your parent object also gets unreferenced, than the GC will determine the whole chain of objects are unreferenced (the Parent object, the event subscription, and the handler object) and will collect them all together on the next collect cycle.
If I may, I'd recommend this MSDN article. Gives you a nice overview of the broad concept of garbage collection in .NET. Very useful to keep in mind while coding.
Program exists the whole time, and it's referring an instance of Parent, and thus Parent will never be garbage collected (until the program ends).
Parent keeps a collection of delegates through it's event handler SomeEvent, so any delegates in there won't be garbage collected.
So in short, no, it won't be garbage collected.
When you run your program, the Parent class holds a reference to the EventHandler delegate instance you're creating in the Handler constructor, and the delegate instance holds a reference to the Handler instance. So the Handler instance will not be garbage collected as long as there is a reference to the Parent instance.

.NET object events and dispose / GC

EDIT: After Joel Coehoorns excellent answer, I understand that I need to be more specific, so I modified my code to be closer to thing I'm trying to understand...
Events: As I understand, in the background the events are "collection" of EventHandlers aka Delegates which will be executed when event raised. So for me it means that if object Y has event E and object X subscribes to event Y.E, then Y will have reference to X, since Y must execute the method located in X, in that way, X can not be collected, and that thing i understand.
//Creates reference to this (b) in a.
a.EventHappened += new EventHandler(this.HandleEvent);
But it is not what Joel Coehoorn tells...
However, there is an issue with events such that sometimes people like to use IDisposable with types that have events. The problem is that when a type X subscribes to events in another type Y, X now has a reference to Y. This reference will prevent Y from being collected.
I not understand how X will reference the Y ???
I modified a bit my example to illustrate my case more closer:
class Service //Let's say it's windows service that must be 24/7 online
{
A _a;
void Start()
{
CustomNotificationSystem.OnEventRaised += new EventHandler(CustomNotificationSystemHandler)
_a = new A();
B b1 = new B(_a);
B b2 = new B(_a);
C c1 = new C(_a);
C c2 = new C(_a);
}
void CustomNotificationSystemHandler(args)
{
//_a.Dispose(); ADDED BY **EDIT 2***
a.Dispose();
_a = new A();
/*
b1,b2,c1,c2 will continue to exists as is, and I know they will now subscribed
to previous instance of _a, and it's OK by me, BUT in that example, now, nobody
references the previous instance of _a (b not holds reference to _a) and by my
theory, previous instance of _a, now may be collected...or I'm missing
something???
*/
}
}
class A : IDisposable
{
public event EventHandler EventHappened;
}
class B
{
public B(A a) //Class B does not stores reference to a internally.
{
a.EventHappened += new EventHandler(this.HandleEventB);
}
public void HandleEventB(object sender, EventArgs args)
{
}
}
class C
{
public C(A a) //Class B not stores reference to a internally.
{
a.EventHappened += new EventHandler(this.HandleEventC);
}
public void HandleEventC(object sender, EventArgs args)
{
}
}
EDIT 2: OK, now it's clear, when subscriber subscribes to a publishers events, it's NOT creates a reference to the publisher in subscriber. Only the reference from publisher to subscriber created (through EventHandler)...in this case when publisher collected by GC before the subscriber (subscribers lifetime is greater then publishers), there's no problem.
BUT...as I know, it's not guaranteed when GC will collect the publisher so in theory, even if subscribers lifetime is greater then publishers, it can happen that subscriber is legal for collection, but publisher is still not collected (I don't know if within closest GC cycle, GC will be smart enough to collect publisher first and then subscriber.
Anyway, in such case, since my subscriber do not have direct reference to publisher and can't unsubscribe the event, I would like to make publisher to implement IDisposable, in order to dispose it before delete all references to him (see in CustomNotificationSystemHandler in my example).
AND AGAIN What I should write in publishers dispose method in order to clear all references to subscribers? should it be EventHappened -= null; or EventHappened = null; or there's no way to do it in such way, and I need to make something like below ???
public event EventHandler EventHappened
{
add
{
eventTable["EventHappened"] = (EventHandler)eventTable["EventHappened"] + value;
}
remove
{
eventTable["EventHappened"] = (EventHandler)eventTable["EventHappened"] - value;
}
}
The life time of object B is longer that A, so A may be disposed earlier
It sounds like you are confusing "Disposal" with "Collection"? Disposing an object has nothing to do with memory or garbage collection. To make sure everything is clear, let's break up the two scenarios, and then I'll move on to events at the end:
Collection:
Nothing you do will ever allow A to be collected before it's parent B. As long as B is reachable, so is A. Even though A is private, it's still reachable from any code within B, and so as long as B is reachable, A is considered reachable. This means the garbage collector doesn't know for sure that you're done with it, and will never collect A until it is also safe to collect B. This is true even if you explicitly call GC.Collect() or similar. As long an object is reachable, it will not be collected.
Disposal:
I'm not even sure why you are implement IDisposable here (it has nothing to do with memory or garbage collection), but I'll give you the benefit of the doubt for the moment that we just don't see the unmanaged resource.
Nothing prevents you from Disposing A whenever you want. Just call a.Dispose(), and it's done. The only way the .Net framework will ever call Dispose() for you automatically is at the end of using block. Dispose() is not called during garbage collection, unless you do it as part of the object's finalizer (more on finalizers in a moment).
When implementing IDisposable, you are sending a message to programmers that this type should (maybe even "must") be disposed promptly. There are two correct patterns for any IDisposable object (with two variations on the pattern). The first pattern is to enclose the type itself in a using block. When this is not possible (for example: code such as yours where the type is a member of another type), the second pattern is that the parent type should also implement IDisposable so it can itself then be included in a using block, and it's Dispose() can call your type's Dispose(). The variation on these patterns is to use try/finally blocks instead of a using block, where you call Dispose() in the finally block.
Now on to finalizers. The only time you need to implement a finalizer is for an IDisposable type that originates an unmanaged resource. So, for example, if your type A above is just wrapping a class like SqlConnection, it does not need a finalizer because the finalizer in SqlConnection itself will take care of any needed cleanup. But, if your type A were implementing a connection to a whole new kind of database engine, you would want a finalizer to make sure your connections are closed when the object is collected. Your type B, however, would not need a finalizer, even though it manages/wraps your type A, because type A will take care of finalizing the connections.
Events:
Technically, events are still managed code and shouldn't need to be disposed. However, there is an issue with events such that sometimes people like to use IDisposable with types that have events. The problem is that when a type X subscribes to events in another type Y, Y now has a reference to X. This reference can prevent X from being collected. If you expected Y to have a longer lifetime then X, you can run into problems here, particularly if Y is very long-lived relative to many X's that come and go over time.
To get around this, sometimes programmers will have type Y implement IDisposable, and the purpose of the Dispose() method is to unsubscribe any events so that subscribing objects can also be collected. Technically, this is not the purpose of the Dispose() pattern, but it works well enough that I'm not going to argue about it. There are two things you need to know when using this pattern with events:
You do not need a finalizer if this is the only reason for implementing IDisposable
Instances of your type still need a using or try/finally block, or you haven't gained anything. Otherwise Dispose() will not be called and your objects still cannot be collected.
In this case, your type A is private to type B, and so only type B can subscribe to A's events. Since 'a' is a member of type B, neither is eligible for garbage collection until B is no longer reachable, at which point both will no longer be reachable and the event subscription reference won't count. That means a reference held on B by A's event would not prevent B from being collected. However, if you use the A type in other places, you may still want to have A implement IDisposable to make sure your events are unsubscribed. If you do that, make sure to follow the whole pattern, such that instances of A are enclosed in using or try/finally blocks.
I have added My comments in your sample code.
class A : IDisposable
{
public event EventHandler EventHappened
{
add
{
eventTable["EventHappened"] = (EventHandler)eventTable["EventHappened"] + value;
}
remove
{
eventTable["EventHappened"] = (EventHandler)eventTable["EventHappened"] - value;
}
}
public void Dispose()
{
//Amit: If you have only one event 'EventHappened',
//you can clear up the subscribers as follows
eventTable["EventHappened"] = null;
//Amit: EventHappened = null will not work here as it is
//just a syntactical sugar to clear the compiler generated backing delegate.
//Since you have added 'add' and 'remove' there is no compiler generated
//delegate to clear
//
//Above was just to explain the concept.
//If eventTable is a dictionary of EventHandlers
//You can simply call 'clear' on it.
//This will work even if there are more events like EventHappened
}
}
class B
{
public B(A a)
{
a.EventHappened += new EventHandler(this.HandleEventB);
//You are absolutely right here.
//class B does not store any reference to A
//Subscribing an event does not add any reference to publisher
//Here all you are doing is calling 'Add' method of 'EventHappened'
//passing it a delegate which holds a reference to B.
//Hence there is a path from A to B but not reverse.
}
public void HandleEventB(object sender, EventArgs args)
{
}
}
class C
{
public C(A a)
{
a.EventHappened += new EventHandler(this.HandleEventC);
}
public void HandleEventC(object sender, EventArgs args)
{
}
}
class Service
{
A _a;
void Start()
{
CustomNotificationSystem.OnEventRaised += new EventHandler(CustomNotificationSystemHandler)
_a = new A();
//Amit:You are right all these do not store any reference to _a
B b1 = new B(_a);
B b2 = new B(_a);
C c1 = new C(_a);
C c2 = new C(_a);
}
void CustomNotificationSystemHandler(args)
{
//Amit: You decide that _a has lived its life and must be disposed.
//Here I assume you want to dispose so that it stops firing its events
//More on this later
_a.Dispose();
//Amit: Now _a points to a brand new A and hence previous instance
//is eligible for collection since there are no active references to
//previous _a now
_a = new A();
}
}
b1,b2,c1,c2 will continue to exists as is, and I know they will now
subscribed to previous instance of _a, and it's OK by me, BUT in that
example, now, nobody references the previous instance of _a (b not
holds reference to _a) and by my theory, previous instance of _a, now
may be collected...or I'm missing something???
As explained through my comments in the above code, you are not missing anything here :)
BUT...as I know, it's not guaranteed when GC will collect the
publisher so in theory, even if subscribers lifetime is greater then
publishers, it can happen that subscriber is legal for collection, but
publisher is still not collected (I don't know if within closest GC
cycle, GC will be smart enough to collect publisher first and then
subscriber.
Since publisher references subscriber, it can never happen that the subscriber becomes eligible for collection before the publisher but reverse can be true. If publisher gets collected before subscriber then, as you said, there is no problem. If the subscriber belongs to a lower GC generation than publisher then since publisher holds a reference to subscriber, GC will treat the subscriber as reachable and will not collect it. If both belong to same generation, they will be collected together.
since my subscriber do not have direct reference to publisher and
can't unsubscribe the event, I would like to make publisher to
implement IDisposable
Contrary to what some have suggested, I would recommend implementing dispose if at any point you are deterministically sure that the object is no longer required. Simply updating an object reference may not always lead to an object stop publishing events.
Consider the following code:
class MainClass
{
public static Publisher Publisher;
static void Main()
{
Publisher = new Publisher();
Thread eventThread = new Thread(DoWork);
eventThread.Start();
Publisher.StartPublishing(); //Keep on firing events
}
static void DoWork()
{
var subscriber = new Subscriber();
subscriber = null;
//Subscriber is referenced by publisher's SomeEvent only
Thread.Sleep(200);
//We have waited enough, we don't require the Publisher now
Publisher = null;
GC.Collect();
//Even after GC.Collect, publisher is not collected even when we have set Publisher to null
//This is because 'StartPublishing' method is under execution at this point of time
//which means it is implicitly reachable from Main Thread's stack (through 'this' pointer)
//This also means that subscriber remain alive
//Even when we intended the Publisher to stop publishing, it will keep firing events due to somewhat 'hidden' reference to it from Main Thread!!!!
}
}
internal class Publisher
{
public void StartPublishing()
{
Thread.Sleep(100);
InvokeSomeEvent(null);
Thread.Sleep(100);
InvokeSomeEvent(null);
Thread.Sleep(100);
InvokeSomeEvent(null);
Thread.Sleep(100);
InvokeSomeEvent(null);
}
public event EventHandler SomeEvent;
public void InvokeSomeEvent(object e)
{
EventHandler handler = SomeEvent;
if (handler != null)
{
handler(this, null);
}
}
~Publisher()
{
Console.WriteLine("I am never Printed");
}
}
internal class Subscriber
{
public Subscriber()
{
if(MainClass.Publisher != null)
{
MainClass.Publisher.SomeEvent += PublisherSomeEvent;
}
}
void PublisherSomeEvent(object sender, EventArgs e)
{
if (MainClass.Publisher == null)
{
//How can null fire an event!!! Raise Exception
throw new Exception("Booooooooommmm");
//But notice 'sender' is not null
}
}
}
If you run the above code, more often than not you will receive the 'Booooooooommmm'. Hence idea is that event publisher must stop firing events when we are sure that its life is up.
This can be done through Dispose method.
There are two ways to achieve this:
Set a flag 'IsDisposed' and check it before firing any event.
Clear up the event subscribers list (as suggested in my comments in your code).
Benefit of 2 is that you release any reference to the subscribers, thereby enabling there collection (as I explained earlier even if the publisher is garbage but belongs to higher generation then it may still prolong collection of lower generation subscribers).
Though, admittedly, it will be quite rare that you experience the demonstrated behavior due to 'hidden' reachability of the publisher but as you can see benefits of 2 are clear and are valid for all event publishers especially long living ones (Singletons anybody!!!). This itself makes it worth to implement Dispose and go with 2.
Contrary to what some other answers claim, events whose publisher's GC lifetime may exceed a subscriber's useful lifetime should be regarded as unmanaged resources. The term "unmanaged" in the phrase "unmanaged resource" doesn't mean "completely outside the world of managed code", but rather relates to whether objects require cleanup beyond that provided by the managed garbage collector.
For example, a collection may expose a CollectionChanged event. If objects of some other type which subscribes to such an event are repeatedly created and abandoned, the collection may end up holding a delegate reference to each and every such object. If such creation and abandonment happens e.g. once per second (as might happen if the object in question were created in a routine that updates a UI window) the number of such references could grow by more than 86,000 for each day that the program was running. Not a big problem for a program which is never run for more than a few minutes, but an absolute killer for a program which could be run for weeks at a time.
It's really unfortunate that Microsoft didn't come up with a better event-cleanup pattern in vb.net or C#. There's seldom any reason why a class instance which subscribes to events shouldn't clean them up before it's abandoned, but Microsoft did nothing to facilitate such cleanup. In practice, one can get away with abandoning objects that are subscribed to events sufficiently often (because the event publisher will go out of scope around the same time as the subscriber) that the annoying level of effort necessary to ensure events get properly cleaned up doesn't seem worthwhile. Unfortunately, it's not always easy to predict all the cases where an event publisher might live longer than expected; if many classes leave events dangling, it's possible for huge amounts of memory to be uncollectable because one of the event subscriptions happens to belong to a long-lived object.
Addendum in response to edit
If X were to subscribe to an event from Y and then abandon all references to Y, and if Y became eligible for collection, X would not prevent Y from being collected. That would be a good thing. If X were to keep a strong reference to Y for the purpose of being able to dispose of it, such reference would prevent Y from being collected. That might arguably not be such a good thing. In some situations it would be better for X to keep a long WeakReference (one constructed with the second parameter set to true) to Y rather than a direct reference; if the target of the WeakReference is non-null when X is Disposed, it will have to unsubscribe from Y's event. If the target is null, it can't unsubscribe but it won't matter, because by then Y (and its reference to X) will have completely ceased to exist. Note that in the unlikely event that Y dies and is resurrected, X will still want to unsubscribe its event; using a long WeakReference will ensure that can still happen.
While some would argue that X shouldn't bother keeping a reference to Y, and Y should simply be written to use some sort of weak event dispatching, such behavior is not correct in the general case because there's no way for Y to tell whether X would do anything that other code might care about even if Y holds the only reference to X. It is entirely possible that X might hold a reference to some strongly-rooted object, and might do something to that other object within its event handler. The fact that Y holds the only reference to X should not imply that no other objects are "interested" in X. The only generally-correct solution is to have objects which are no longer interested in other objects' events notify the latter objects of that fact.
I would have my class B implements IDisposable as well and in it's dispose routine, I would first check whether A is not null and then dispose A. By using this approach you have to just make sure to dispose the last of your class and the internals will handle all other dispose.
MSDN Reference
"To prevent your event handler from being invoked when the event is raised, unsubscribe from the event. In order to prevent resource leaks, you should unsubscribe from events before you dispose of a subscriber object. Until you unsubscribe from an event, the multicast delegate that underlies the event in the publishing object has a reference to the delegate that encapsulates the subscriber's event handler. As long as the publishing object holds that reference, garbage collection will not delete your subscriber object."
"When all subscribers have unsubscribed from an event, the event instance in the publisher class is set to null."
You don't need to unhook event handlers when disposing of an object, although you may want to. By that I mean that the GC will clean up event handlers just fine without any intervention on your part, however depending on the scenario you may want to remove those event handlers before the GC does in order to prevent the handler being called when you weren't expecting it.
In your example I think you have your roles reversed - class A shouldn't really be unsubscribing event handlers added by others and has no real need to remove event handlers eiether, as it can instead just stop raising those events!
Suppose however that the situation is reversed
class A
{
public EventHandler EventHappened;
}
class B : IDisposable
{
A _a;
private bool disposed;
public B(A a)
{
_a = a;
a.EventHappened += this.HandleEvent;
}
public void Dispose(bool disposing)
{
// As an aside - if disposing is false then we are being called during
// finalization and so cannot safely reference _a as it may have already
// been GCd
// In this situation we dont to remove the handler anyway as its about
// to be cleaned up by the GC anyway
if (disposing)
{
// You may wish to unsubscribe from events here
_a.EventHappened -= this.HandleEvent;
disposed = true;
}
}
public void HandleEvent(object sender, EventArgs args)
{
if (disposed)
{
throw new ObjectDisposedException();
}
}
}
If its possible for A to continue raising events even after B has been disposed, and the event handler for B could do something that may cause either an exception or some other unexpected behaviour if B is disposed then its probably a good idea to unsubscribe from this event first.
The object A references B through EventHandler delegate(A has an instance of EventHandler wich references B). B don't have any reference to A. When A is set to null it will be collected and the memory will be freed.
So you don't need to clear anything in this case.

Is unwired event a memory leak?

If yes, is there a way to unwire it globally for all wired events
Edit: Say for example. I have objects, each tagged with an event like orm.NatureChanged += Nature_Changed; I tag these events when i create each orm instance. If I didn't unwire by means like orm.NatureChanged -= Nature_Changed; will it cause memory leak?
Regardless of what you're asking, the technical answer to your question is "no". Technically, unless you discover a bug in the CLR, there are no true "memory leaks" with managed objects (that's a big part of what makes them a Good Thing). To answer what I think you're actually asking, though, it sounds like you're asking one of two things:
Is there something that needs to be done with events that do not have any delegates attached to them?
Can an event prevent objects from getting cleaned up by the garbage collector?
The answer to the first question is simply "no". Move along, nothing to see here.
The answer to the second has been discussed at length here on SO and other areas of the web. The short version is that an attached event handler means that the GC will consider the target instance as "reachable" by the event-firing instance. This can cause objects to remain in memory longer than expected, since this reachability is somewhat transparent to the user (developer) because of the way delegates are constructed.
In other words, say I have two objects: Producer and Consumer. Producer fires an event that Consumer...consumes.
public class Producer
{
public event EventHandler OmgIDidSomething;
}
public class Consumer
{
public void AttachTo(Producer producer)
{
producer.OmgIDidSomething += new EventHandler(producer_OmgIDidSomething);
}
private void producer_OmgIDidSomething(object sender, EventArgs e)
{
// ...
}
}
In this example, any instance of Consumer where AttachTo is called will remain reachable as far as the GC is concerned until the instance of Producer that it attached to is eligible for collection, because the delegate behind the implementation of the OmgIDidSomething event has a reference to the instance of Consumer that it corresponds to.
No, because when you unwire an event, the delegate (it's an object) which was wired to the event is no longer rooted, and will be collected when the GC sees fit to do so. This is assuming of course the event delegate isn't attached to multiple handlers, in which case it won't be collected until it is unwired from all events.
If you meant whether events that don't get unwired can cause a memory leak, the answer is that it can if the actual lifetime of the object holding the event delegate is much longer than the useful lifetime of the object to which the delegate refers. For example, if a collection's enumerator hooked a CollectionChanged event, and someone were to get enumerators without ever disposing them, then every time the collection was enumerated (without disposing the enumerator) a new enumerator object would be created that would stay in memory as long as the underlying collection.
It's not a memory leak, it just doesn't hook any handlers to that event if they aren't wired, automatically or otherwise. So the event fires to nobody, gets cleaned up, and life goes on.
This conversation talks on this subject: How do events cause memory leaks in C# and how do Weak References help mitigate that?
See some background information here: What does AutoEventWireUp page property mean?

How to get rid of event handlers safely?

Let's say I have a class A which can fire an event called X. Now I have a class B and in a method I get an instance to A and bind the event to a handler in B:
public void BindEvent(A a)
{
a.X += AEventHandler;
}
I have three questions about this.
Is it true that when I now set the reference to the B instance to null, it won't be garbage collected since the garbage collector thinks it's still in use (thus keeping a useless and potentially interfering copy of B in memory).
What about when I have another object c (of class C) in which I have a reference to A called a ("this.a = new A()"). Then I call "b.BindEvent(this.a)", and in c I set the reference to a to null ("this.a = null"). Will this keep the copy of A in memory because it's referenced through the event in b?
If either or both are true of the above, how can I best circumvent these issues? If I have a whole list of event handlers (say 10 lines like "a.SomeEvent += SomeMethod") should I clean them all up again ("a.SomeEvent -= SomeMethod"). At which time or place in the code should I do these things?
Well it's gotten a bit fuzzy but I'm not sure how to explain in a better way. Please leave a comment if I need to explain something more detailed.
so: A is the publisher and B is the subscriber?
first bullet: if B is the instance with AEventHandler - then it is still in use, so no, it won't get collected unless the a instance is unreachable.
second bullet: huh? (will read again...) If the A and B instances are both unreachable, they will be garbage collected; the event doesn't matter. If A is reachable, then B will stay alive. However, the event subscription never keeps A alive; it is one way... A can keep B alive, but B doesn't keep A alive. Does that cover it?
third bullet: in most cases, the two things have similar life expentency, so it isn't an issue. It only becomes an issue if the thing publishing the event lives a lot longer than the things with the handlers. In which case, you simply need to religiously clean up after yourself - for example: a.X -= AEventHandler. In particular, static events are evil for this reason.
You should really unbind the event handler before destroying the class instance that it relates to. (Using your code as an exmaple.)
public void UnbindEvent(A a)
{
a.X -= AEventHandler;
}
I would also ask, why are you setting class variables to null?
Correct.
Correct.
Why would you want to circumvent this behaviour? It's how the GC is designed to work. If you need to do some clean up when each object is torn down, use the Dispose pattern.
Yes, event are references, if you don't unregister, the object implementing the event handler will not be garbage collected.
You can do this:
Remove all registered event, if A knows when they are not used anymore.
class A
{
// clearing all registrations
private void ClearEvents()
{
X = null;
}
}
Or you unregister in B, if B knows when it doesn't use it anymore. You need to keep a reference to a to be able to unregister.
You could also implement IDisposable.
class B : IDisposable
{
private A registeredToA;
public void BindEvent(A a)
{
registeredToA = a;
registeredToA.X += AEventHandler;
}
public void Dispose()
{
registeredToA.x -= AEventHandler;
}
}
This is a breaking change to your code, because B needs always to be disposed.

Categories