Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Finalization

0.00/5 (No votes)
25 Mar 2004 1  
Reasons *not* to implement Finalize

Introduction

This document assumes you are familiar with the �Dispose/Finalize� design pattern, which I�ve discussed in GC101 and GC102.

Microsoft invented the pattern of finalization with the sole purpose of making code safer. If a developer referencing a component instance forgets to dispose off the component properly (by calling Dispose), the component will still be disposed off automatically by the GC.

Let me discuss a few negative effects of implementing Finalize (these do not exist for components without finalizers):

  1. Object needs to be enlisted in �finalisation queue�.
  2. Object needs to be removed from �finalisation queue�.
  3. Object needs to be enlisted in �to be finalized queue�.
  4. Component�s Finalize method needs to be called.
  5. Object needs to be removed from �to be finalized queue�.
  6. You have no way of telling when exactly the GC will call your finalizer.

These points give you a broad overview of the impact a Finalize method has behind the scenes. As you can see: finalization has a big performance impact. Let�s discuss finalizers more in depth.

A single Finalization thread

The most significant performance clog is point 4 above. The .NET garbage collector (GC) has a single worker-thread that calls all the Finalize methods of everything enlisted in the �to be finalized queue�. This thread runs with high priority, and if many components need finalization, this thread will block threads with a lower priority from executing. With lots of garbage to be collected, you are effectively launching a denial-of-service attack against your processor!

Enlisting / de-listing

Enlisting / de-listing in the queues mentioned above has a hit on performance. This could be acceptable for simple components, but the only way to have tighter control over the way it impacts on performance is by doing cleanup manually.

You have no control over when the GC fires your Finalize() method. You do know when it is suitable for a cleanup: when the resource in question is no longer needed, and the application is in such a state that it can absorb the performance hit of cleaning up. By placing cleanup code in Dispose() or Close() methods, you can cleanup manually at the most appropriate time.

No referenced objects

You can�t reference any named object in a Finalize() method. Since there is no specific order in which objects are being finalized, there is no way of telling whether the object you wish to reference has already been GC�d. This leaves you with a somewhat limited capability in your finalizer.

Overall degraded garbage collection

It will take the GC more cycles to collect a finalizable object. The impact of this is greater than it initially seems. Not only will your object live longer, but also all other objects it has references to.

As the GC places your object in the �to be finalized� queue, it will also be promoted to Generation 1 of the GC. Generation 1 is collected less often than Generation 0, and so all managed / unmanaged resources referenced by your object will effectively hog the system memory more than necessary.

Why would anyone implement finalization?

Are there any motivations for it? Sure there are: to *ensure* cleanup of your object�s resources. This is the one and only motivation for implementing finalizers. If your component uses resources, they need to be freed after use.

If a developer using your component calls Dispose() explicitly (assuming you call GC.SuppressFinalize in the Dispose method), the resources will be cleaned, and you have no worries! If a developer however forgets to dispose off your component explicitly, the GC thread will clean up the resources for him when it calls Finalize. Since the time of finalization is random, this takes control out of your hands.

Conclusion

I see the road branching into two:

  1. Putting all cleanup code in your Dispose (or Close) method puts a lot of responsibility on the shoulders of developers using your component. This is true especially if your component utilizes unmanaged resources, where failure to call Dispose will result in memory leaks. The flipside is that it also empowers your developers to Dispose off your object completely when most appropriate.
  2. Implement Finalize, and only clean unmanaged resources in the method. Clean all managed resources in the Dispose method.

The most widely accepted way is number 2. I beg to differ � let me tell you why:

If developers fail to call Dispose on your component, the resultant memory leaks will surely be spotted during testing. (If not, the testing process needs a revision!) After spotting a leak, code can be changed to call Dispose on your component, and a system with optimum performance is the product.

There are even a few complications in the way Finalize is implemented in some of the framework classes, of which I shall write in a further article - keep an eye on my blog for an update.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here