Disposal & Garbage Collection in C#

shabbir's Avatar author of Disposal & Garbage Collection in C#
This is an article on Disposal & Garbage Collection in C# in C#.
Garbage collection and memory management is probably that domain of programming where .NET Framework beats most of the other application development platforms. Most of the time, garbage collection is automatically done by .NET runtime once the object gets out of scope. When a .NET object is not being referenced by any module in the application, the garbage collector automatically disposes it after some time. However, this is not the case with all the objects. There are some objects that do not release resources implicitly and you have to incorporate a piece of code in your application to make them release resources. These objects include, open files, operating system handles, locks and unmanaged code. In .NET Framework, the process of explicitly releasing memory and resources is called disposal. The managed code is implicitly released and is referred as garbage collection.

The simplest difference in garbage collection and disposal is that the former is done implicitly most of the time by the CLR while the latter is done via explicit code written by the developer. You think of the disposal as mechanism through which open files, operating system handles and locks are released. On the other hand, memory management and other tasks are implicitly done by the garbage collector. This article explains the basic of both of these concepts.

IDisposable, Dispose and Close



In .NET Framework the types that need to explicitly release the resources, an IDisposable interface has been defined which contains a Dispose method which is called by the type when you want it to release resources. The IDisposable interface looks like this:
Code:
public interface IDisposable
{
    public void Dispose();
}
Now suppose that you have a FileStream object which is referring to some open file. In order to release that file when you have completed your task, you can write following code:
Code:
FileStream filereader = new FileStream("file.txt", FileMode.Open);
try {
	// Write some content to the file.txt
}
finally {
	if (filereader !=null) {
		filereader.Dispose();
	}
}
Here we have got a FileStream class that implements the IDisposable interface. Therefore, we can explicitly release resources held by the object of this class by calling Dispose on this class. We encapsulate our code in Try and Finally block and after we have done writing to the file in Try block, the Finally block will execute and in this Finally block we check that if the FileReader object is not already null, call its Dispose method to release its resources. The purpose of Try block is to make it sure that even if an exception occurs, the Finally block will be called and the resources would be released.

There is a short hand code notion for the above code. The C# contains a “using” statement which automatically calls Dispose method on types that are enclosed within “using” statement. Behind the scenes, the Try, Finally black is implemented but is hidden from the user to make code succinct and readable. Here is how, the above code can be implemented via “using” statement.
Code:
using (FileStream filereader = new FileStream("file.txt", FileMode.Open))
{
    // Write some content to the file.txt
}
Standard Dispose Rules

There are three basic rules that must be kept in mind while disposing an object via Dispose method.
  1. An object once disposed can’t be reactivated and its properties and methods can’t be accessed.
  2. If an object wraps a child object, then calling Dispose on the parent or wrapper object would also call Dispose on the child object.
  3. You can call Dispose on an object as many times as you want.

Stop & Close Methods

In addition the Dispose methods, Close and Stop methods are also used for releasing resources. The Close method performs the same functionality as the Dispose method or often times, the Close method performs functionality which is subset of the Dispose method. For example, consider IDbConnection interface. If you call Close on the object of this type, you can latter on reopen the connection, however if you call Dispose, you can never reopen that connection. Similarly, the Stop method is used to release resources from objects like Timer and HttpListener, but Stop method allows restarting the resource unlike the Dispose method.

When to and when NOT to Dispose



Any object or a wrapper object that references or handles an unmanaged resource such a stream or a database connection, a windows form control or similar object would need to call Dispose in almost all the cases if you want them to free resources. Or in other words, most of the types that implement the IDisposable interface have a handle that corresponds to some unmanaged code, therefore they need to call Dispose in order to release those unmanaged resources.

On the other hand, if any of the following scenarios is true, you should NOT dispose the object:
  • When you get an object via static property or field and the object is shared, you should not call the Dispose method. Because in that case, multiple resources are referring one object and few of them might still need the object. For instances the objects in System.Drawing namespace are often obtained by calling static property or field such as in case of Brushes.Red. These objects should not be disposed because they are used throughout the program.
  • The second case is when you have an object but calling Dispose method on it would do something that you do not want. In such cases, the Dispose method should not be called on the object.
  • When there is no need to call Dispose method and adding Dispose to your object is unnecessary because it would add further complexity to your code.
You can also clear object’s field when Dispose method is called on the object. This is done inside the Dispose method. This is particularly useful when an object has subscribed to events and we want to unsubscribe our object so that we do not receive unnecessary notifications. Similarly, if an object is holding secure encrypted data and you want to dispose that data so that it might not get leaked to unauthorized resources, disposing encrypted data fields will help.

Automatic Garbage Collection



Disposal refers to the release of resources that are considered “outside” the application domain. Disposal is done implicitly via Dispose, Close or Stop methods. But still, whether an object implements or not the Dispose method, the memory occupied by it on the heap has to be released at some stage. Luckily for us, this task is done automatically by the garbage collector.

To further understand the concept of garbage collection, have a look at the following piece of code:
Code:
public void Print()
{
    string[] names = new string[100];
    //print names
}
Here we have a print method. When this method executes, memory for an array to hold 100 strings is allocated on the heap. The array is referenced by a variable ‘names’. This name variable would be stored on the local variable stack. When complete method is executed, the ‘names’ variable is removed from the stack. Now there is no reference to the actual 100 strings allocated on the heap memory. This array is called orphan array and since it doesn’t have any reference, the garbage collector would know it and remove this array from the memory resulting in automatic memory management.

When Garbage Collector runs?



A point worth consideration here is that garbage collector doesn’t remove an object from memory as soon as it becomes. Garbage collector runs after a certain period of time and removes all the orphan objects from the memory. This is more like a real life garbage collector that visits daily or weekly to collect garbage from homes and offices etc. However, garbage collector doesn’t have a fixed period of time after it runs. The CLR decides when to run the garbage collector and this decision usually depends upon three factors: The available memory, the memory occupied by orphan objects and the time since the last time garbage collector operated. This time can vary from nanoseconds to many days or weeks.

Understanding Roots

In simplest terms, roots are anything that keeps object alive and prevent them from becoming orphan and being collected by the garbage collector. If an object is not being referenced by any root, either directly or indirectly, it will be collected by garbage collector. Root can be:
  • A local variable in a method that is being executed. (In our last code sample, local variable ‘names’ was a root).
  • Any static variable.
  • Object which stores other objects ready for finalization and is located on the queue.
Finalizers

Finalizers can be thought of as destructors in C#. Finalizer is basically a method that is always executed before an object is removed from the memory by the garbage collector. The syntax of a Finalizer is similar to the constructor of the class but a “~” sign is prefixed with the constructor. Have a look at the following piece of code:
Code:
class Customer
{
    ~Customer()
    { }
}
Here we have a class customer that contains a Finalizer. An important point to note here is that Finalizers cannot be declared static or public and they cannot call their base class. Also, Finalizer cannot have any parameters.

Garbage collection shows a very interesting behavior towards the objects with Finalizers. When garbage collector runs, it looks for all the orphan objects in the memory. Garbage collector immediately deletes all those objects that DO NOT contains Finalizer. All the objects with Finalizers are placed into a separate queue and are not deleted at the moment. After garbage collection completes its execution, the remaining program instructions are executed and along with that in a separate thread the Finalizer methods of all the objects from the queue start executing. The queue here acts as a root and as soon as the Finalizer method of an object in this queue executes, the object becomes completely orphan and in the next garbage collection cycle, this object is removed from the memory.

Garbage Collection Optimization Techniques



Although, you can call System.GC.Collect any time to start a garbage collection process however it is better to let CLR decide when to start deleting orphan objects. Still, there are few optimization techniques that can be used to reduce the time between garbage collection cycles. These techniques include:

Generational Collection

This concept works on the principle of logical heap memory division into three parts. The first part is called Gen0 and it contains all those objects that have just been allocated. The second portion of memory is called Gen1 and it contains those objects that have survived one garbage collection cycle while the Gen2 section contains all the rest of objects.

The idea here is that Gen0 section is the smallest memory section and when it gets full, Gen0 collection instigates which delete all the orphan objects from Gen0 section. The objects that survive are shifted to Gen1 section. Similarly, when Gen1 section gets full, the Gen1 garbage collection occurs and all the orphan objects are removed from the heap, the remaining objects are shifted to Gen2. The idea, here is that the Gen2 objects are long-lived and it is not a good idea that garbage collector checks them again and again about whether they need to be removed or not. So, the most frequent removal is from Gen0 and least frequent from Gen2 which significantly reduces garbage collection time.

Large Object Heap

The idea of Large Object Heap (LOH) is similar to Generational Collection. More time and effort is required to remove a large object from the memory and at times it makes smaller objects wait for longer periods of time to remove. A solution for this is to allocate a separate heap for these large objects and then remove objects from all the collections following a round robin or any other scheduling technique.

Concurrent and Background Collection

In concurrent collection, objects from different heap sections are concurrently removed increasing garbage collection efficiency. Background collection refers to the removal of objects from heap in a background thread. The application remains more responsive this way.

In this article we explained basics of both disposal and garbage collection. These two terms are often interchangeably used but there is a striking contrast between the two. We explained the difference between the two and then threw some light on fundamental features of both. I would advise to implement these concepts in your application and you would yourself see the improvement in performance of your applications. Also, for more interesting articles on .NET and C#, keep visiting this site.