I have this piece of code
public class Publisher
{
public event EventHandler SomeEvent;
}
public class Subscriber
{
public static int Count;
public Subscriber(Publisher publisher)
{
publisher.SomeEvent += new EventHandler(publisher_SomeEvent);
}
~Subscriber()
{
Subscriber.Count++;
}
private void publisher_SomeEvent(object sender, EventArgs e)
{
// TODO
}
}
In the Main method of my application I have
static void Main(string[] args)
{
Publisher publisher = new Publisher();
for (int i = 0; i < 10; i++)
{
Subscriber subscriber = new Subscriber(publisher);
subscriber = null;
}
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(Subscriber.Count.ToString());
}
If I run this, I will have 0 as output. If I remove event subscriptions from the code, I will got the expecting result – which is 10.
When GC.Collect() is being called, gc is being forced to start garbage collection. Because Subscriber has Finalize defined in it, GC will suspend collection until finalizequeue is empty – that is after all Subscription instances will call its Finalize() methods ( Please correct me if my assumptions are wrong). At the next line GC.WaitForPendingFinalizers() is called which will effectively suspend execution until finalizer queue is empty. Now, because we have 0 as output I believe Finalize() is not being called, which makes me believe that GC didn’t mark subscriber instances to be collected, thus Finalizer() methods are not being called.
So I have 2 questions
My only guess is that since there are 10 instances of Subscriber that are referencing to the same publisher instance, when GC collection occurs, it sees that there are other references to publisher, thus it can’t be collected, and as a result all subscription instances alongside with publisher are being moved to the next generation, so garbage collection doesn’t occur nor Finalize() is being called at the time code execution reaches to Console.WriteLine(Subscriber.Count.ToString())
Am I right or am I missing something here?
Yes, an event source won't be garbage collected until it is closed - until then, it may receive events from the network and is kept alive for that.
Garbage Collection is a form of automatic memory management. It is a special case of resource management, in which the limited resource being managed is memory. Benefits for the programmer is that garbage collection frees the programmer from manually dealing with memory allocation and deallocation.
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.
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.
The answer to both questions is Yes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With