# Sunday, 23 November 2003
« C# Destructor Considered Harmful | Main | Finalization and the JIT »
More On Finalization

After posting my previous entry about C# destructors, I came up with a better implementation of them.

What if, instead of depending on the derived class to be well behaved we could enforce that the base class destructor is always called?

It turns out that this is possible, thanks to the extremely powerful capabilities the CLR has for dealing with methods.

Here is an example C# class:

  class Foo {
    ~Foo() {
      // release unmanaged resource
    }
  } 

The current C# compiler compiles this as:

  class Foo {
    protected void Finalize() {
      try {
        // release unmanaged resource
      } finally {
        base.Finalize();
      }
    }
  }

As I pointed out in my previous entry, the problem with this construct is that it depends on any derived class to have a similar Finalize method (that ensures the base class Finalize gets called). However, not all languages have such a feature built-in like C#. For example, in VB.NET it is trivial (and an easy mistake to make) to override Finalize and to forget to call the base class implementation.

It occurred to me that you could also compile the example class as follows (pseudo code):

class Foo {
  private void .dtor() overrides Object.Finalize {
    try {
      Finalize();
    } finally {
      // release unmanaged resource
    }
  }
  protected new virtual void Finalize() {
  }
}

Note that this uses the CLR's ability to override a method even though the method name is different and the ability to introduce a new virtual method with the same name, but a different vtable slot.

Now, you don't rely on the derived class to have a correct Finalize method, because even if the derived class' Finalize method doesn't call the base class method, the finally block in .dtor will still run.

For a few minutes I was happy with myself for coming up with this clever trick, but I soon realized that while it was a little better, it still did nothing to prevent malicious code from calling Thread.Sleep() in their overridden Finalize method.

It turns out that there is an even bigger problem with this solution, though. The current design guidelines for cleaning up unmanaged resources actually suggest that you call a virtual method from your destructor that does the actual cleanup. This, of course, reintroduces the problem that the derived class can override that method and not call the base class.

In summary, it's a big mess. My original (Java) suggestions for writing a safe class that wraps an unmanaged resource are here and they apply to .NET classes in the same way. My suggestions to Microsoft are:

  • Fix the guidelines. Managed and unmanaged cleanup are two very different things and should not be mixed. Also, add a rule not to call any virtual methods from a Finalize method.
  • Make overriding Object.Finalize require full trust. There is no need for a finalizer unless you are wrapping an unmanaged resource and only fully trusted code can do that.
  • Change the C# compiler to make the Finalize method generated for the destructor sealed by default.

These are all compatibility breaking changes, so they are probably not going to happen. Resource leaks and denial of service attacks are not on the agenda for the mainstream CLR. It's interesting to note that Yukon apparantly prohibits untrusted code from overriding the Finalize method, I wonder how they'll deal with the dispose pattern.

Sunday, 23 November 2003 15:48:06 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
Name
E-mail
Home page

I apologize for the lameness of this, but the comment spam was driving me nuts. In order to be able to post a comment, you need to answer a simple question. Hopefully this question is easy enough not to annoy serious commenters, but hard enough to keep the spammers away.

Anti-Spam Question: What method on java.lang.System returns an object's original hashcode (i.e. the one that would be returned by java.lang.Object.hashCode() if it wasn't overridden)? (case is significant)

Answer:  
Comment (HTML not allowed)  

Live Comment Preview