Garbage Collection (2), Manage UnManaged Code

Part I of this article described the core concepts of garbage collection, and the process of the memory management of .NET for managed code. This article will discuss how to manage the unmanaged code in .NET, the Dispose Pattern.
 

Introduction

 
Cleaning Up Unmanaged Resources: manage and clean up unmanaged resources.
 
The CLR provides support for automatic memory management (GC). However, resources other than managed memory still need to be released explicitly and are referred to as unmanaged resources, such as the wrap operating system resources files, windows, network connections, or database connections.
 
The GC was specifically not designed to manage such unmanaged resources, which means that the responsibility for managing unmanaged resources lies in the hands of the developers.
 
There are wo ways to clean the unmanaged resource,
  • Automatic way by Finalizer and GC 
  • Manual way by Dispose Pattern
 
 

A: Automatic way by Finalizer and GC

 
System.Object declares a virtual method Finalize that is called by the GC before the object’s memory is reclaimed by the GC and can be overridden to release unmanaged resources. Types that override the finalizer are referred to as finalizable types
  • The Finalize method is called automatically after an object becomes inaccessible, unless you have protected the object against finalization.
  • The Finalize method is called as the very last operation before the application terminates, and it is nondeterministic. Normally, most of Finalize methods will run at some other point during the run of the application and not at the very end.
Note:
  • Destructor
    • C# doesn’t strictly support destructors
    • C# does support overriding the Object.Finalize method that is syntactically identical to a destructor as ~Classname.
    • Compiler wraps all code in the ‘destructor’ (finalizer) or the Finalize override in a try block and the compounding finally block is calling the base class object.Finalize
  • Drawbacks of using Finalizer
    • The finalizer is called when the GC detects that an object is eligible for collection. This happens at some undetermined period of time after the resource is not needed anymore. The delay between when the developer could or would like to release the resource and the time when the resource is actually released by the finalizer might be unacceptable.
    • When the CLR needs to call a finalizer, it must postpone collection of the object’s memory until the next round of garbage collection (the finalizers run between collections). This means that the object’s memory (and all objects it refers to) will not be released for a longer period of time.

B: Manual Way by Dispose Pattern

 
  • Implement the dispose pattern. This requires that you provide an IDisposable.Dispose implementation to enable the deterministic release of unmanaged resources. A consumer of your type calls Dispose when the object (and the resources it uses) is no longer needed. The Dispose method immediately releases the unmanaged resources.
  • Provide a way for your unmanaged resources to be released in the event that a consumer of your type forgets to call Dispose. There are two ways to do this,
    • Override the Object.Finalize method (in concept).
    • Use a safe handle to wrap your unmanaged resource (in practice).
 
Note:
 
The Object.Finalize method overriding is good to demostrate the concept that we will discuss in this article.  However, Microsoft warns that this way "can be complex and error-prone". In practice, we should use Safe handle method that we will discuss in the next article, Part III,  in order for us to concentrate on the concept of dispose method here.
 
 
We will discuss the following one by one,
  • 1: IDisposable interface;
  • 2: Dispose() method;
  • 3: GC.SuppressFinalize method;
  • 4: Override the Object.Finalize method
  • 5: Disposable Types;
  • 6: Consumer of the Desposable Type;
1 - IDisposable interface
 
The Framework provides the System.IDisposable interface
  1. public interface IDisposable  
  2. {  
  3.     void Dispose();  

that only includes one method: Dispose();
 
2 - Dispose() Method
 
The IDisposable interface should be implemented the method, Dispose(), to provide a manual way to deterministicly release unmanaged resources as soon as they are not needed.
  1. public void Dispose()  
  2. {  
  3.     ...... // Dispose the unmanaged resources
3 - GC.SuppressFinalize method
 
The Dispose Method also provides the GC.SuppressFinalize method that can tell the GC that an object was manually disposed of and does not need to be finalized anymore, in which case the object’s memory can be reclaimed earlier.
  1. public void Dispose()  
  2. {  
  3.     ...... // Dispose the unmanaged resource
  4.     GC.SuppressFinalize(this);  
4 - Override the Object.Finalize method
 
Finalization enables the non-deterministic release of unmanaged resources when the consumer of a type fails to call IDisposable.Dispose to dispose of them deterministically. Define a finalizer by overriding the Object.Finalize method.
  1. public class MyClass    
  2. {    
  3.    ~MyClass()    
  4.    {    
  5.       // Do unmanaged resource clean up here    
  6.     
  7.       Console.WriteLine("In destructor");    
  8.    }    
  9. }    
Note
 If we just simply overrode the Finalize method,
  1. public class MyClass    
  2. {    
  3.    protected override void Finalize()    
  4.    {    
  5.       // Do unmanaged resource clean up here    
  6.    }    
  7. }  
it won't work, you will get a compiling error:
 
 
The suggestion is "Do not override object.Finalize. Instead, provide a destructor." Just as we discussed in A, "C# does support overriding the Object.Finalize method that is syntactically identical to a destructor as ~Classname."
 
5 - Disposable Types
 
Types that implement the IDisposable interface are referred to as disposable types
  1. public class MyClass : IDisposable  
  2. {  
  3.     private string name;  
  4.     public MyClass(string name) { this.name = name; }  
  5.     override public string ToString() { return name; } 

  6.    
  7.     // call Dispose() in Finalizer, i.e., 'Destructor'
  8.     ~MyClass()   
  9.     {   
  10.         Dispose()
  11.         Console.WriteLine("~Thing()");   
  12.     } 
  13.   
  14.     // Implementation of IDisposable.  
  15.     // Call the virtual Dispose method.  
  16.     // Suppress Finalization.  
  17.     public void Dispose()  
  18.     {  
  19.         Console.WriteLine("Dispose()");  
  20.         GC.SuppressFinalize(this);  
  21.     }  
6 - Consumer of Disposable Types
  1. public class GarbageDisposalApp  
  2. {  
  3.     [STAThread]  
  4.     public static void Main(string[] args)  
  5.     {  
  6.         DoSomething();  
  7.         Console.WriteLine("end of Main");  
  8.            Console.ReadLine();  
  9.     }  
  10.     public static void DoSomething()  
  11.     {  
  12.         MyClass t = new MyClass("Foo");  
  13.         Console.WriteLine(t);  
  14.            t.Dispose();  // toggle this to see the difference
  15.            t = null;  
  16.         GC.Collect();  
  17.         GC.WaitForPendingFinalizers();  
  18.     }  
Notes for dispose pattern
  • The Dispose Pattern is intended to standardize the usage and implementation of finalizer and the IDisposable interface to free foreign resources.
  • The main reason for the Dispose pattern is to enable the client of an object to Dispose it manually, while making sure that the object is Disposed exactly once during its lifetime.

Summary

 
This article discussed how to manage the unmanaged code in .NET, the Dispose Pattern. 
 
Dispose Pattern in short:
  • Basically, you need to call Dispose from the destructor, and in the Dispose you should Suppress the Finalize
  • When you properly implement a Dispose method, the Finalize method becomes a safeguard to clean up resources if the Dispose method is not called.
Difference between Finalize and Dispose Pattern in short,
  • The Finalize method will be called at some point during the run of the application before the very end point, automaticallyThe dispose method must be invoked manually
  • To avoid after running the dispose manually and then run the finalizer automatically again, or missing to run the dispose, we should call the dispose method from the finalize method, and suppress the Finalize method by GC.SuppressFinalize from the dispose method.
In this article, we concentrate on the concept, using the overriding the Object.Finalize method, we will discuss in details for safe handle method in the next article, Part III.