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
- Is my assumption right and event subscription prevents GC to mark subscriber instances to be collected?
- If so, it is because publisher holds reference to subscriber? (Garbage collector and event handlers)
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?
Publisher.SomeEvent
delegate is being called it will execute whatever is subscribed to it at the moment of the call. This means that the subscriber instances has to be alive while the subscription is active. That's why Publisher has to keep a reference to all the subscribers. Since subscribers are reachable GC can't collect them. – Andrew Savinykh