Sunday, December 11, 2011

Disposing - doing it the right way

.NET’s Garbage-Collection (GC) is a well-documented and discussed feature of the Common-Language-Runtime (CLR). Plenty of posts and tutorials can be found online.
This topic is almost always followed by of another popular aspect - ”Finalize\Dispose Pattern”.

However, there are dark corners that usually aren’t mentioned widely enough, although they play important role in performance. While disposing seems as a simple plug-and-play, overusing it can affect the performance of CPU-bounded applications.

This post will try to shed some light on disposing dangers and show some best practices.

GC and disposing in a nutshell
.NET CLR holds managed heap for each application-domain where reference-types (objects) are stored, while their pointing variables are located on the stack. Those variables are often referred to as “roots”. Every once in a while (according to specific rules), CLR executes a thread which scans the heap, identifies unreachable (“dead-root”) objects and clears them from memory.
Microsoft published a GC optimization technique, the “Finalize\Dispose Pattern”, which is recommended to read before moving on with this post.
Objects can clear both managed and unmanaged members before their root vanishes, so GC won’t need to dedicate precious resources for finalizing later.
This technique is even enforced by Code-Analysis tools (such as FxCop), but bad news is that it causes programmers to often miss important key-points.

Here are the basic rules to watch out:

DO NOT implement Finalizer automatically
A major mistake is to implement needless Finalizer, and it happens quite a lot.
Class must implement such if and only if it creates unmanaged resources which do not reside on heap.
Implementing needless Finalize method means it will take extra time to allocate the object as a reference must be inserted into the finalization queue.
Moreover, each and every object allocated for this type will require two GC collections and additional finalization step, since there is still a finalization-root reference!

Those aren’t things to underestimate – imagine scenarios where hundreds of objects are allocated & deallocated per second!

Other two common mistakes, are that 1) Dispose method is called by GC to clear memory, and 2) Calling Dispose manually prevents objects even from going into GC.
That’s totally wrong!
Dispose can be called only by programmer’s code – either directly or via “using” block.
GC on the other hand clears the memory by physically detecting and removing dead-objects and later executing Finalizers if needed.
So, objects must go into one GC at least.
Implementing Dispose is only recommended when type has Finalizer or at least one of its members is IDisposable. Such preliminary disposing (and finalization suspension) will prevent the object and its members from consuming second GC plus finalization.
Do not make your class IDisposable unnecessarily since it will enforce classes which hold it to become also IDisposable, and so on...

Additional best practice which is usually forgotten is to ensure disposing doesn’t throw exceptions. We use nice extension method for that sake:
public static class IDisposableExtensions
  public static void SafeDispose(this IDisposable disposedObj) 
       if (disposedObj != null)
    catch {} // log some stats here… 

Last common mistake (but not least), is that calling Dispose method marks objects as null, i.e. removes their root reference.
Such thing never happens!
Removal of root happens only when the variable pointing to the object expires (end of method for ex.), and only then GC is aware of it and marks it for cleaning. So, if one wishes to optimize GC for disposed objects, we recommend using the following nice trick – setting reference variables to “null” right after their disposal. That way, in case GC collection is about to run, such objects will be collected right away and won’t wait for their variable to be eliminated by CLR.
Integrating the two techniques described above, our virtual disposing methods usually look like this (a simple snippet can be used):
private bool m_IsDisposed = false;
protected virtual void Dispose(bool disposing)
   if (!m_IsDisposed) 
      if (disposing)  
      {   // clean managed resources...      
         m_ClassMember = null;  
      // clean unmanaged resources here...  
      m_IsDisposed = true; 

No comments:

Post a Comment