#931 – Objects with Finalizers Take Longer to Garbage Collect

The purpose of the garbage collector is to discover objects on the managed heap that are no longer reachable and to reclaim the memory occupied by these objects.

Once an object is discovered to be no longer reachable, the garbage collector checks to see if the object has a finalizer.  If the object doesn’t have a finalizer, it is marked for collection and its memory is reclaimed by the garbage collector.

If an object has a finalizer, it will not be marked for garbage collection, but instead be marked for finalization.  It will not get garbage collected during the current pass of the garbage collector.  Instead, its finalizer will be run at some point after garbage collection finishes.  It will then be eligible for collection during the next pass of the garbage collector.

Reclaiming memory for objects with finalizers therefore requires at least two passes of the garbage collector.

#879 – Unhandled Exceptions in Finalizers

Like other unhandled exceptions, an exception that is thrown from a finalizer and not handled will cause your application to terminate.

~Dog()
{
    Console.WriteLine(
       "Dog finalizer is running.  I'll throw an exception and your app will terminate");
    throw new Exception("Dang it");
}
Dog d = new Dog("Kirby", 15);
d.Bark();

d = null;
GC.Collect();

879-001

#832 – The Sequence in Which Finalizers Are Called

When you implement a finalizer, using the destructor syntax, the finalizer needs to always call the finalizer of its base class.  The C# compiler will automatically take care of this, invoking the finalizer of the base class within a finally block to ensure that it gets called.

Because the call to the finalizer of the base class occurs within a finally block, it will get called after all of the code within the derived class’ finalizer has executed.  This means that the sequence in which the finalizers are called is from the most derived class up the inheritance chain.

For example, if Terrier inherits from Dog, code in the Terrier’s finalizer will execute before code in the Dog’s finalizer.

 

 

#739 – Avoid Accessing an Object After Its Been Finalized

A finalizer should not create a new reference to the object being finalized.  Below is an example where doing this leads to the ability to reference the object after it’s been finalized.

This is an example of questionable code–in general, it’s dangerous to reconstitute an object after it’s been finalized.

In the Dog class, we save a reference to the object being finalized.

    public class Dog
    {
        public static Dog KeepDogRef;

        public string Name { get; set; }

        public Dog(string name)
        {
            Name = name;
        }

        ~Dog()
        {
            Console.WriteLine("Dog destructor for " + Name + " called");
            Dog.KeepDogRef = this;
        }

        public void Bark()
        {
            Console.WriteLine(Name + " : Woof");
        }
    }

In main program, we reconstitute a Dog object after it’s been finalized.

            Dog dog = new Dog("Bowser");

            WeakReference dogRef = new WeakReference(dog);

            // Unref Bowser
            dog = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();

            // Bowser is gone
            Console.WriteLine(string.Format("Object still alive: {0}", dogRef.IsAlive));

            // Hey, Bowser is alive again!
            Dog newRef = Dog.KeepDogRef;
            newRef.Bark();

739-001

#737 – When to Implement a Finalizer

In C#, you implement a finalizer using the destructor (~) syntax.  The finalizer is called by the system when the object is being destroyed.

In general, you rarely need to implement a finalizer.  Rules of thumb for implementing a finalizer include:

  • Implement a finalizer only when the object has unmanaged resources to clean up (e.g. file handles)
  • Do not implement a finalizer if you don’t have unmanaged resources to clean up
  • The finalizer should release all of the object’s unmanaged resources
  • Implement the finalizer as part of the dispose pattern, which allows for deterministic destruction
  • The finalizer should only concern itself with cleanup of objects owned within the class where it is defined
  • The finalizer should avoid side-effects and only include cleanup code
  • The finalizer should not add references to any objects, including a reference to the finalizer’s own object
  • The finalizer should not call methods in any other objects

#672 – An Exception Thrown From a Finalizer Will Be Treated as an Unhandled Exception

If an exception originates in a finalizer in C# and is not handled within the body of the finalizer, the exception will be treated by the application as an unhandled exception.  If you have no mechanism for dealing with unhandled exceptions, this will lead to a crash.

In the example below, the exception originating in the Dog finalizer is not caught within the Main method.

        static void Main()
        {
            try
            {
                Dog d = new Dog();
                d = null;

                GC.Collect();
                GC.WaitForPendingFinalizers();

                Console.WriteLine("I made it to the end of the program");
                Console.ReadLine();
            }
            catch (Exception xx)
            {
                Console.WriteLine(string.Format("Hey, I caught an exception! {0}", xx.ToString()));
            }
    public class Dog
    {
        public Dog()
        {
            Console.WriteLine("Dog constructor");
        }

        ~Dog()
        {
            Console.WriteLine("Dog finalizer running");
            throw new Exception("** Exception from Dog finalizer **");
        }
    }

#646 – Value Types Don’t Have Finalizers

In .NET, you override the Object.Finalize method to dispose of any unmanaged resources when the object is being garbage collected.  In C#, you write this finalizer using the destructor (~) syntax.  You can also implement the Dispose pattern to allow users of your object to deterministically dispose of  resources.

You cannot implement a finalizer or the dispose pattern for a value type.  A finalizer makes no sense for a value type because value typed objects are not garbage collected–they simply die when they go out of scope.

Since the finalizer and the dispose pattern exist for the purpose of releasing unmanaged resources, and you can’t implement either for a value type, you should avoid using unmanaged resources in value types.  A user would have to explicitly call some method to do the cleanup and there is no guarantee that they would remember to call the method.