According to Fundamentals of garbage collection, all threads except the one that triggered the garbage collection are suspended during garbage collection. Since the finalizers are called during the garbage collection process, I would expect the thread to be suspended until all the finalizers are executed. Thus, I would expect the following code to just "take longer" to complete. Instead, it throws a System.OutOfMemoryException
. Could someone elaborate on why this happens?
class Program
{
class Person
{
long[] personArray = new long[1000000];
~Person()
{
Thread.Sleep(1);
}
}
static void Main(string[] args)
{
for (long i = 0; i < 100000000000; i++)
{
Person p = new Person();
}
}
}
Wouldn't the creation of new Person
objects on the heap also be suspended while the finalizer is being executed?
Code taken from Exam Ref 70-483 Programming in C# (MCSD)
Okay, so I did some more research, and it turns out that the garbage collector does not call the finalizers synchronously. It does the following:
After that, the finalizer thread starts running in the background along with the rest of the application, and calls the finalizers of each object in its queue. This is where the issue I described arises. The Person
objects in the heap have their references moved into the finalization queue, but their memory is not freed up until the finalization actually happens. The finalization takes place while the application is running (and more objects are created on the heap).
The garbage collector cannot reclaim the memory until finalization is over, thus, a race condition arises between the Main Thread (creating the objects) and the GC Finalizer Thread (calling the finalizers).
I have also confirmed this behaviour by debugging the IL code and seeing the execution switch between the two threads described above.
I have also found this question on SO which describes the same behaviour.
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