C# and Resource Management: Best Practices for Handling Unmanaged Resources

3 Minute(s) to read

C# is commonly used on Windows with the Common-Language Runtime which is provided by the .NET Framework, an execution environment and interpreter that executes, and translates code from JIT-compiled IL instructions to native assembly instructions. The Common-Language Run-time additionally handles memory management for heap and stack allocations, and garbage collection where possible, however there are still instances where memory leaks can occur.

Resource streams and file handles can be allocated and opened and left in such state for the entire duration of an application's lifetime unless they are explicitly disposed of. This is regardless of whether or not the garbage collection procedure has highlighted a GC root (i.e. reference to heap allocated memory) as being suitable for deletion. This blog post intends on describing the best methods of leveraging the GC supported routines such as finalizers, and other C# programming conventions for ensuring that unmanaged resources allocated properly disposed of.

Using the Finalizer

For the sake of simplicity, the "finalizer" could be considered synonymous with the destructor in C++, with the caveat that it cannot be explicitly invoked. When memory must be freed from the heap, it is for the garbage collector to do so. The finalizer is a routine or function that is invoked when an object is being destroyed and subsequently deallocated or freed from memory. When defined in a C# class, the garbage collector during a collection routine will invoke the finalizer if the GC root for a particular chunk of memory no longer has any references. There is no explicit requirement for it to be defined (unlike a constructor for example).

    /// <summary>
    ///     An example class.
    /// </summary>
    public class TestClass
    {
		public TestClass()
		{
		
		}
		
		~TestClass()
		{
			// This gets invoked by the garbage collector when it is time to delete this from the heap.
		}
    }

Implementing IDisposable

Should a C# class or object hold references to unmanaged resources (file handles, thread management primitives, network connections etc.), it must provide an interface for releasing these resources once it is no longer required. Not doing so can lead to undefined behaviour, especially if other running process on the system are dependent on acquiring access to particular resource handles (global system mutexes, and files).

Combining Finalizers with IDisposable

You don't want to find yourself in a situation where an object improperly releases any associated unmanaged resources once it is removed from the heap by the garbage collector. When the object is freed from the heap, or explicitly disposed of during the application's lifecycle, it must be ensured that unmanaged resources are freed otherwise you could find yourself dealing with unexpected behavior. More documentation as to when and how the garbage collection routine works can be found here.

The key difference to understand between the finalizer and the dispose method is that one is invoked automatically by the garbage collector, and the other is invoked explicitly at some point in the application's life-cycle.

Examples of unmanaged resources that must be disposed:

  • Thread locking primitives (mutexes, read/write locks etc.).
  • Network connections.
  • Handles to files on disk.

Regardless of how the cleanup is invoked, unmanaged resources that are associated with an object must be released and disposed of.

An Implementation

Find below an interface that implements the IDisposable interface.

    /// <summary>
    ///     
    /// </summary>
    public interface IFinalizable : IDisposable
    {
        /// <summary>
        ///     
        /// </summary>
        /// <param name="disposing"></param>
        void Dispose(bool disposing);

        /// <summary>
        ///     
        /// </summary>
        bool IsDisposed { get; }
    }

Resources



Comments