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.
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
- public interface IDisposable
- {
- 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.
- public void Dispose()
- {
- ...... // 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.
- public void Dispose()
- {
- ...... // Dispose the unmanaged resource
- 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.
- public class MyClass
- {
- ~MyClass()
- {
-
-
- Console.WriteLine("In destructor");
- }
- }
Note
If we just simply overrode the Finalize method,
- public class MyClass
- {
- protected override void Finalize()
- {
-
- }
- }
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."
Types that implement the
IDisposable
interface are referred to as
disposable
types.
- public class MyClass : IDisposable
- {
- private string name;
- public MyClass(string name) { this.name = name; }
- override public string ToString() { return name; }
-
- ~MyClass()
- {
- Dispose();
- Console.WriteLine("~Thing()");
- }
-
-
-
-
- public void Dispose()
- {
- Console.WriteLine("Dispose()");
- GC.SuppressFinalize(this);
- }
- }
6 - Consumer of Disposable Types
- public class GarbageDisposalApp
- {
- [STAThread]
- public static void Main(string[] args)
- {
- DoSomething();
- Console.WriteLine("end of Main");
- Console.ReadLine();
- }
- public static void DoSomething()
- {
- MyClass t = new MyClass("Foo");
- Console.WriteLine(t);
- t.Dispose(); // toggle this to see the difference
- t = null;
- GC.Collect();
- GC.WaitForPendingFinalizers();
- }
- }
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, automatically. The 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.