In reviewing some code, I came across a simple copy/paste bug that led to counter intuitive behavior. The intent was to subscribe to System and Application event log events and process them both. The boiled down code below looks to me as though the first event producer, for Application log, gets replaced and therefore shouldn't produce any more events after garbage collection. However, what I observe is that any writes to the application log will still trigger the ApplicationEventLog_EntryWritten method.
I presume then that something is holding onto a reference to the original EventLog instance but I would like to understand what and why?
class Program
{
public static EventLog ApplicationEventLog;
static void Main(string[] args)
{
ApplicationEventLog = new EventLog("Application");
ApplicationEventLog.EnableRaisingEvents = true;
ApplicationEventLog.EntryWritten += ApplicationEventLog_EntryWritten;
//new instance should have been assigned to new variable
//public static EventLog sytemEventLog;
ApplicationEventLog = new EventLog("System");
ApplicationEventLog.EnableRaisingEvents = true;
ApplicationEventLog.EntryWritten += SystemEventLog_EntryWritten;
//for illustrative purposes
//Original EventLog instance is no longer assigned to anything so I would expect it to get GC'd
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
Console.WriteLine("Press ESC to stop");
do
{
while (!Console.KeyAvailable)
{
Task.Delay(250);
}
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
}
private static void ApplicationEventLog_EntryWritten(object sender, EntryWrittenEventArgs e)
{
Debug.WriteLine("Application log written to");
}
private static void SystemEventLog_EntryWritten(object sender, EntryWrittenEventArgs e)
{
Debug.WriteLine("System log written to");
}
}
UPDATED EXAMPLE:
This version takes all eventlog code out of the picture for a hopefully clearer example:
class Program
{
static void Main(string[] args)
{
var producer = new Producer();
producer.SomeEvent += EventConsumer1;
//Obviously unsubscribing before reassignment works but why does the subscription prevent GC of this first instance?
//producer.SomeEvent -= EventConsumer1;
producer = new Producer();
producer.SomeEvent += EventConsumer2;
Console.WriteLine("Press ESC to stop");
do
{
while (!Console.KeyAvailable)
{
//for illustrative purposes
//Original producer instance is no longer assigned to anything so I would expect it to get GC'd
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
Task.Delay(250);
}
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
}
private static void EventConsumer1(object sender, EventArgs e)
{
Debug.WriteLine("I would have expected this to get hit once at most");
}
private static void EventConsumer2(object sender, EventArgs e)
{
Debug.WriteLine("Should hit this periodically");
}
}
public class Producer
{
public event EventHandler SomeEvent;
public Producer()
{
SendEventsAsync();
}
private async void SendEventsAsync()
{
while (true)
{
SomeEvent?.Invoke(this, new EventArgs());
await Task.Delay(5000);
}
}
~Producer()
{
Debug.WriteLine("Instance being garbage collected");
}
}