Do Event Handlers Stop Garbage Collection from Occurring

Do event handlers stop garbage collection from occurring?

For the specific question "Will pClass be garbage collected": the event subscription has no effect on the collection of pClass (as the publisher).

For GC in general (in particular, the target): it depends whether MyFunction is static or instance-based.

A delegate (such as an event subscription) to an instance method includes a reference to the instance. So yes, an event subscription will prevent GC. However, as soon as the object publishing the event (pClass above) is eligible for collection, this ceases to be a problem.

Note that this is one-way; i.e. if we have:

publisher.SomeEvent += target.SomeHandler;

then "publisher" will keep "target" alive, but "target" will not keep "publisher" alive.

So no: if pClass is going to be collected anyway, there is no need to unsubscribe the listeners. However, if pClass was long-lived (longer than the instance with MyFunction), then pClass could keep that instance alive, so it would be necessary to unsubscribe if you want the target to be collected.

Static events, however, for this reason, are very dangerous when used with instance-based handlers.

Why event handlers prevent garbage collector from occurring

You are mis-identifying what is really going on, a very common trap in C#. You'll need to run the Release build of your test program and run it without the debugger (press Ctrl+F5). The way it will run on your user's machine. And now notice that it completely doesn't matter anymore whether or not you subscribe the event, you will always get 10.

The issue is that, when you use a debugger, the publisher object doesn't get collected. I explained the reason for that in detail in this answer.

Expanding a bit on that, you have circular references here. The Subscriber objects reference the Publisher object. And the Publisher object has references to the Subscriber objects. Circular references are not sufficient to keep objects alive. And thank goodness for that, garbage collection would not be very effective if that was the case. The publisher object must be referenced elsewhere to stay alive, the local variable isn't good enough.

How to keep event subscriptions from blocking garbage collection

You should look at the Weak Event Patterns :

https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/weak-event-patterns

Do event handlers stop garbage collection if the hanlder is in a seperate assembly?

It is the same regardless of which assembly the classes originate from.

Garbage collector and event handlers

It wouldn't cause a leak - the GC can handle circular references with no problems.

However, it would mean that the publisher would effectively have a reference to the subscriber, so the subscriber couldn't be garbage collected until either the publisher is eligible for GC, or it unsubscribes from the event.

Event handler affects in garbage collection in CLR

In summary

It is not the event itself that is causing this, but rather referencing an instance member of class A from a long running thread using a closure.

The event itself is not the issue, or is it?

This code a1.timeChange += A1_timeChange; causes a delegate inside class A that implements the event public event EventHandler timeChange to reference A1_timeChange inside class B.

So, the reference is the other way around

In your scenario, if you got rid of all references to class B, but did not unsubscribe from the event, then the event handler in A that points to a handler method in B could keep class B reachable, and therefore not GC'ed

What is really happening

Your class A is accessible from one of the GC roots (specifically, actively running threads), it's still reachable and therefore, not collected - read more

You have spawned an eternally running task (well it will stop when the app closes / foreground thread terminates) with this code

Task.Factory.StartNew(() => {
while (true)
{
timeChange(DateTime.Now, null);
System.Threading.Thread.Sleep(3000);
}

The thing is, that you're using a lambda that closes over the timeChange event, and that makes a compiler to generate a class that simply references class A

One more thing, just by having a descructor (which is compiled to a finalizer) you prolong the lifetime of your object by one GC collection - on the first GC, your object will be marked as unreachable, and will be put onto finalization queue. And on the next GC, the finalizer will actually run - read more

Will an anonymous-delegate event listener prevent garbage collection?

The following test suggests that, no, the scenario does not result in a memory leak.

public partial class LeakTest : UserControl
{
public ICommand PopupCommand { get; private set; }

public LeakTest()
{
InitializeComponent();

PopupCommand = new DelegateCommand(arg =>
{
var child = new ChildWindow();
child.Closed += (sender, args) =>
{
System.Diagnostics.Debug.WriteLine("Closed window");
};

// when the window has loaded, close it and re-trigger the command
child.Loaded += (sender, args) =>
{
child.Close();
PopupCommand.Execute(null);
};
child.Show();
});
}
}

The reason is suggested in the answer to the (Winforms) post linked to by Jwosty:

In your example, the publisher only exists within the scope of your private method, so both the dialog and the handler will be garbage collected at some point after the method returns.

In other words, the memory-leak concern is really the other way around -- the event publisher (the ChildWindow control) holds a reference to the subscriber (the DelegateCommand), but not the other way around. So, once the ChildWindow is closed, the garbage collector will free its memory.

Can event listener of WeakEvent be deleted by garbage collection any time?

Yes, if the WeakReference is the only thing which holds a reference to ListenerObject, then ListenerObject can be GC'd at any point.

You would use a pattern like this if your EventWrapper class is not the only thing which has a reference to ListenerObject, but your EventWrapper doesn't know when that other thing is going to release its reference to ListenerObject.

For example, ListenerObject might be a UI control which appears on a screen, and the EventWrapper might be owned by a singleton service. The UI control will stay alive as long as that screen is shown, but will be released when the user changes the screen. The service might not know when that happens. Using a weak event pattern means that you don't accidentally end up with a memory leak in this case.


Note, if you do want to implement the weak event pattern, use a WeakEventManager as detailed in this article.



Related Topics



Leave a reply



Submit