Drawing Blanks

Premature Optimization is a Prerequisite for Success

Finalize this.

leave a comment »

3 years ago I ran into an issue with suppressed finalization. I wrote a small essay on it which I sent to my coworkers and friends. I thought I’d rewrite it and publish, in case others are interested.

Summary: If you inherit from a class that calls GC.SuppressFinalize(this) in the constructor then your finalizer will never execute. To fix, call GC.ReRegisterForFinalize(this) in your constructor.

The Code

Let’s inherit a class from DataTable which will own some disposable resource.

NOTE: In this example I chose a SqlConnection, but this is an extremely bad idea. SqlConnections must be scoped to the method, and only to the method, no exceptions. The reasons are: 1. It is a good pattern and practice. 2. If you don’t follow this good pattern, Connection Pooling will punish you! (https://bbzippo.wordpress.com/2009/11/05/net-connection-pooling-enforces-good-coding-patterns/ , http://social.msdn.microsoft.com/Forums/en-US/adodotnetdataproviders/thread/74a5db87-c63f-4386-b317-e848dedb2cd9)

Ok, back to subject. Our class will allocate the resource in the constructor and close it in the destructor (finalizer). We are going to observe something weird:

class ConnectedDataTable: DataTable
{
    private SqlConnection connection;

    public ConnectedDataTable() : base()
    {

        // This connection will close when the object goes out of scope 
        //   and gets GC'd. 
        // HAHA, NOT REALLY!.
        connection = new SqlConnection("connString");
        connection.Open();
    }

    ~ConnectedDataTable()
    {
        // Problem! This finalizer will never execute! But Why?!
        connection.Close();
    }   

    // other methods...
    // ...
}
 

Explanations

Let me first remind you what the .Net Garbage Collector does with unused (unreferenced) objects. The GC will put them into the finalization queue. The finalization thread will then iterate through them and call the finalizers. And during the next GC cycle the finalized objects will be destroyed (deallocated).

However if you call GC.SuppressFinalize(myObject) then your object will bypass the finalization queue and will be destroyed right when the GC picks it up. Normally it is recommended to call SuppressFinalize() in IDisposable.Dispose() after disposing of all unmanaged resources (see MSDN on the IDisposable pattern)

One may think: “What happens to objects that don’t utilize any unmanaged stuff, don’t need IDisposable, and don’t implement finalizers? They probably still wait in the Finalization Queue taking up memory and CPU time – what a waste! So why don’t I call GC.SuppressFinalize(this) right in the constructor to speed things up?”

In most cases this is not a great idea. The runtime is smart enough to recognize objects that do not override Object.Finalize() (or implement destructors in C#) and not put them into the queue for finalization.

But guess what: There is at least one class in the .Net framework that calls GC.SuppressFinalize(this) in the constructor. It is DataTable.

Why? DataTable inherits from System.ComponentModel.MarshalByValueComponent that overrides Finalize(). So the runtime will mark it as requiring finalization.

DataTables are usually expected to be large and they allocate only managed memory. And the work performed by the parent’s Finalize() is probably not needed for DataTable (I did not investigate this deeper, but I trust Microsoft on this).

So it does make sense to suppress finalization in DataTable’s constructor.

But when we inherit a class from DataTable which does allocate some resources and implements a finalizer that disposes of those resources we run into a trouble. The constructor of this class calls the base constructor. And the base constructor will suppress finalization. So our finalizer will never execute.

How do we fix our code? Simply add GC.ReRegisterForFinalize(this) to the constructor.

NOTE: actually it would be better not to inherit from DataTable at all, but rather contain it, but that’s a different topic.

Lessons learned

  1. In some special cases it may be possible to improve performance by suppressing finalization in the constructor.
  2. Extreme caution should be used when inheriting from classes that suppress finalization.
  3. DataTable is an example of questionable design: it introduces non-standard behavior that can propagate to child classes, and doesn’t make consumers aware of that. One option would be to seal the class. Or at least mark it with some interface or attribute that would make the developer (and possibly the compiler) aware of the special behavior.
Advertisements

Written by bbzippo

11/16/2009 at 7:50 am

Posted in programming

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: