In the course of a discussion in chat, I wrote this console application.
Code:
using System;
class Program
{
static void Main(string[] args)
{
CreateClass();
Console.Write("Collecting... ");
GC.Collect();
Console.WriteLine("Done");
}
static void CreateClass()
{
SomeClass c = new SomeClass();
}
}
class SomeClass
{
~SomeClass()
{
throw new Exception();
}
}
Result:
Collecting... Done
Unhandled Exception: System.Exception: Exception of type 'System.Exception' was
thrown.
at SomeClass.Finalize()
I would have expected the app to crash before Done
was printed.
I don't care much about how to make it. My question is, why doesn't it?
Objects with finalizers cannot be collected within a single garbage collection procedure. Such objects are moved to
f-reachable
queue, and remain there until finalizers are called. Only after that they can be garbage-collected.Following code is better, but you should not rely on it anyway:
Also, throwing exceptions in finalizer seems too brutal for me, even for testing purposes.
Also, interesting side-effect of finalizers: an object with finalizer can still 'resurrect' itself (effectively prevent garbage collection of itself), if stores
this
reference in finalizer (assigns it to some static variable).If the above two points are clear, then your code holds a reference to SomeClass in a static method. That means it is still alive until the Program's main method is executing.
If you want your app to crash before printing 'done', then first nullify your SomeClass object and then call GC.Collect. It will put your object in finalizer queue but again its GC's wish when to clear out that queue. If you want GC to clear out that queue and call finalizer, then call GC.WaitForPendingFinalizers(). Your thread will wait until your finalizer is called and it will then proceed. I modified your code for desired output. Instead of throwing exception I printed a statement in finalizer.
In the most common garbage-collector implementations, no managed user code can be run during a garbage-collection cycle.
Finalize
methods count as user code. Although it would be theoretically possible for the system to freeze all other user code whileFinalize
methods execute, this behavior would increase the apparent cost of garbage collection on multi-core systems and also increase the likelihood of deadlock. To avoid these issues, the system does not runFinalize
methods as part of garbage collection, but instead builds a list of objects that need to have theirFinalize
methods run (the list is called the "freachable queue"). The list itself is considered a rooted reference, so any object which is referred to by an object in the freachable queue will be considered to be strongly rooted at least until such time as the system retrieves the freachable object from the queue, runs itsFinalize
method, and discards the reference.Microsoft's early documentation regarding finalization is very confusing, since it suggests that objects to which finalizable objects hold references might not exist when those methods run. In fact, all such objects are guaranteed to exist; what is uncertain is whether they will have already had their
Finalize
methods run.Did you read the documentation?
It is not a command, it is a request, which may or may not work out as you would like. This is not often a good idea anyway (sometimes many, many small, short-lived objects are created as a result of some process, in which case it may be beneficial to call
GC.Collect
, but this is rare).Since it doesn't seem like you are trying to solve a real problem and are instead toying around with the GC this is the best advice I have to offer.