FW: Dispose and Finalize

FW from http://www.dotnetspark.com/kb/2694-non-deterministic-destructors-c-sharp.aspx

 

 

Introduction

This article provides an introduction to destructors in C# and reveals the non-deterministic destructor problem.

It also analyzes the IDisposable, garbage collection, using statement, etc.

Destructor

The destructor is a special purpose method in a class like a constructor. The constructor is called by the runtime after creating an instance. It is meant for initialization purposes. Like that, a destructor is called by the runtime before destroying an instance. It is meant for finalization operations.

A destructor is declared like a constructor with a prefix tilde(~) in C#. A destructor do not have access modifiers like publicprivate, etc.

public class Shape
{
    ~Shape() // Destructor
    {
    }
}

In C#, the .NET runtime automatically destroys any class instances that are no longer in reference. It does this during the process of garbage collection.

Using of destructors will make the application slow in performance.

Finalizers

It is important to note that the destructors are also called Finalizers because the compiler converts a C# destructor into a method named Finalize() in the Intermediate Language (IL). The code in the destructor will be wrapped in a try..finally block inside the Finalize() method.

Garbage Collection

This is the process of freeing up of unused memory of objects periodically. The .NET runtime does the process of garbage collection on the following conditions:

  • When there is shortage of memory
  • Developer invoked the GC.Collect() method

The garbage collection helps the developer to free up object instances.

But remember, garbage collection only destroys the managed resources, i.e. resources created inside the managed memory.

What is the Need of Destructors?

Since the runtime is managing the destruction of all objects, a question may arise: Why do we need destructors?

Answer: To destroy unmanaged resources.

The unmanaged resources are those created outside the managed environment. They may include file handles, network connections, etc. These resources created should be destroyed by the class itself, because the runtime is not able to destroy them.

Therefore, we use destructors to free up the unmanaged resources. This will ensure that all the unmanaged resources created by the class will be freed in the destructor.

E.g.: Let Channel be a class using unmanaged resources having a constructor and a destructor.

public class Channel
{
    public Channel()
    {
        // Create unmanaged resource
        // Lock("c:\\file.log");
    }

    ~Channel()
    {
        // Destroy unmanaged resource
        // Unlock("c:\\file.log");
    }
}

The Channel class locks a file "c:\file.log" in the constructor and unlocks it in the destructor.

Here arises a problem that we cannot predict when the destructor is called. So the file remains locked until the garbage collection, even if the object instance is out of any reference.

For the above problem, we need some method to free the unmanaged resource. In .NET, there is an interface named IDisposable for the purpose.

IDisposable

The IDisposable interface contains a method Dispose() which should be implemented by the class. So we can move freeing up of unmanaged resources to this Dispose() method.

Again there is a problem, that a typical .NET developer not calling the Dispose() method after the usage of class instance. In that case, we need the call to free unmanaged resources both in the destructor and in the Dispose() method.

We can also use a Close() method instead of implementing the IDisposable interface. But, enabling the IDisposable makes our class usable in the using {}statement of C#.

The using statement will automatically call the Dispose() method after exiting the scope of using. (Please refer to the sample code.)

public class Channel : IDisposable
{
    public Channel()
    {
        // Create unmanaged resource
        Lock("c:\\file.log");   
    }
    ~Channel()
    {
        // Destroy unmanaged resource
        Unlock("c:\\file.log");
    }
    void IDisposable.Dispose()
    {
        Unlock("c:\\file.log");
    }
    public void Lock(string file) { } 
    public void Unlock(string file) { }
}

At first look, the above code solves all the problems.

But, it creates a new problem due to the non-deterministic destructors of C#.

Non-Deterministic Destructors

In C#, we do not know when the destructor is executed. It is called only during the garbage collection process and we do not know when it actually happens. This state is called non-deterministic destructors or InDeterministic destructors.

The following figure illustrates the problem. Our 'Channel' class is using some unmanaged resources and freeing them in the Dispose() method and also in the destructor.

There are two cases when the Channel class is used:

  1. A developer creates an instance and exits without calling Dispose()
  2. A developer creates an instance and calls Dispose() 

 

Solution using GC.SuppressFinalize(this)

We can instruct the garbage collector not to call the destructor by using the GC.SuppressFinalize(this) method. So, using this in the Dispose() method after freeing up unmanaged resources will solve the non-deterministic destructor problem.

The solution is given below.

Here, the Channel class has freed up code both in the destructor and in the Dispose() method. And if the developer calls the Dispose() method, the unmanaged resources will be freed and the destructor will be disabled by using GC.SuppressFinalize(this);.

If the developer does not call the Dispose() method, the unmanaged resources are freed up in the destructor by the garbage collector. The figure below illustrates the solution:

Note: The Dispose() method is used to free up the unmanaged resources, and calling Dispose() will not free up the instance. It just executes whatever statements are written in it.

Using the Code

The code along with this article demonstrates the case of using/not using Dispose(), and also the initiating of the GarbageCollection process usingGC.Collect(). Please analyze the Channel class to see the details involved.

  

posted on 2011-02-22 07:57  Joe Hou  阅读(228)  评论(0编辑  收藏  举报