Click here to Skip to main content
15,881,139 members
Articles / Programming Languages / C#
Article

GC 101

Rate me:
Please Sign up or sign in to vote.
4.33/5 (38 votes)
29 Feb 20044 min read 100.3K   50   12
An introduction to programming for the Garbage Collector.

Introduction

I see questions regarding the GC on a daily basis. Finalize vs. Dispose is a popular topic. Let's get it in the clear.

All of us .NET programmers make use of the Garbage Collector (GC) – some without knowing so. You have to familiarize yourself with the internals of the GC if you wish to create scalable components – there is no other option. Even though it does all its bits automagically, you can harness a lot of its power by understanding three basics: Finalize, Dispose, and the Destructor protocol in managed code. I will not be going into the finer details of how the GC does its job, but rather explain how you as a programmer can (and should) optimize your objects for the GC.

Finalize

When an object is instantiated, the GC allocates memory for it on the managed heap. If the class contains a Finalize method, the object is also enlisted in the “finalisation queue”. When this object is no longer needed, its memory will be reclaimed (freed) by the GC. If the object is enlisted in the finalization queue, its Finalize method will be called before discarding of the object. The purpose of the Finalize method is to release any resources (like a database connection, or a handle on a window) that might be in use by your object.

Since the GC decides when it is best to clean up objects (and it does a damn fine job in doing so!), you have no way of telling when exactly Finalize will be called. Finalize is also a protected member and can thus not be called explicitly. Does this mean that cleaning up your object is left entirely in the hands of the GC?

Dispose

Of course not. For increased performance, it is best to cleanup your unused resources immediately after using them. For instance, as soon as you have retrieved your data through a database connection, the connection should be discarded of since it eats up system resources like memory, which could be better utilized by objects that you do in fact need. For this reason, an object can implement the Dispose method (by implementing the IDisposable interface). Calling the Dispose method on an object does two things. Firstly, it cleans up any resources that were in use by your object. Secondly, it marks the object so that the GC would not call its Finalize method when it collects it – the resources have been cleaned up already in your Dispose method. This way, you save the overhead of the GC’s call to Finalize, and you can clean up your object at the most appropriate time.

How to implement Finalize and Dispose

Now that you know the reasons for these two methods, let’s see how to implement it.

In managed code, you cannot override or even call Finalize – it is implicitly generated (in IL) by the compiler if you have a destructor for your object. In other words, the following:

C#
~MyClass
{
    //Cleanup code here
}

Translates to the following:

C#
protected override void Finalize()
{
    try
    {
        //Cleanup of used resources
    }
    finally
    {
        base.Finalize();
    }
}

As you can see, the method also calls Finalize on its parent type, and the parent type will call Finalize on its parent type – the whole hierarchy of your object is thus cleaned up. It is important to understand that you should only have a destructor for your class if it is really necessary, since calling Finalize, and enlisting objects that implement Finalize in the finalization queue by the GC, has significant performance implications.

The Dispose method is publicly callable. (I’ll explain the overload that accepts the boolean parameter later). Here is an example of an object that implements IDisposable:

C#
public class MyClass : IDisposable, BaseClass
{
    bool disposed; //Indicates if object has been disposed

    //Constructor, where your resources might be instantiated
    public MyClass()
    {
        disposed = false;
        //Other Constructor code here
    }
 
    //Destructor, that would imply a Finalize method as noted above
    ~MyClass()
    {
        //Dispose is called, passing false, so that only
        //unmanaged resources are cleaned up. 
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        //Prevent the GC to call Finalize again, since you have already
        //cleaned up.
         GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        //Make sure Dispose does not get called more than once,
        //by checking the disposed field
        try
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    //Clean up managed resources
                }
                //Now clean up unmanaged resources
            }
            disposed = true;
        }
        finally
        {
            base.Dispose(disposing);
        }
    }
}

When you explicitly call Dispose() on your object, both managed- and unmanaged resources will be cleaned up. When the GC cleans your object (instead of you), only unmanaged resources will be cleaned, since the managed resources will be cleaned up by the GC when necessary.

Final Notes

Don’t reference any managed resources in your Finalize method (destructor), since Finalizers are not called in any particular fashion, and the object you reference may thus be disposed of already. In such a case, your Finalize method will fail. If you *do* reference any managed resources downward in your object hierarchy, those objects will not be finalized with the current GC collection, and performance will suffer.

When calling any method on your object, it is necessary to first check if the object has been disposed. So a method in MyClass would look like this:

C#
//In MyClass:
public void MyMethod()
{
    if (this.disposed)
    {
        throw new ObjectDisposedException();
    }
    //Method code goes here
}

In a further article, I will dive deeper into the GC, and explain the implications of threading on your Finalize and Dispose methods.

(This article is also published on my blog.)

[Update: Check GC 102 for further notes on programming for Garbage Collection.]

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
South Africa South Africa
Ernst develops software for the agricultural industry.
He moderates the SADeveloper INETA community, and gives talks on development-related stuff from time to time.
He is an MVP in Visual C#, but whilst he advocates the adoption of .NET, he is also skilled in non-Microsoft technologies.
Sometimes he chats about it.

Comments and Discussions

 
GeneralNice article, but a small question Pin
Jaggu_Aztecsoft17-Feb-08 18:16
Jaggu_Aztecsoft17-Feb-08 18:16 
GeneralHey ..this is copied from... Pin
RavindraThakur21-Dec-06 23:59
RavindraThakur21-Dec-06 23:59 
AnswerRe: Hey ..this is copied from... Pin
Ernst Kuschke23-Feb-09 4:38
Ernst Kuschke23-Feb-09 4:38 
GeneralDoes dispose method dispose referenced objects!! Pin
gonecase26-Oct-04 18:54
gonecase26-Oct-04 18:54 
GeneralManaged vs. Unmanaged Pin
dwurtz9-Mar-04 6:09
dwurtz9-Mar-04 6:09 
GeneralRe: Managed vs. Unmanaged Pin
Ernst Kuschke10-Mar-04 20:21
Ernst Kuschke10-Mar-04 20:21 
Generalbug report Pin
MyBlindy2-Mar-04 17:46
MyBlindy2-Mar-04 17:46 
GeneralRe: bug report Pin
Ernst Kuschke2-Mar-04 19:40
Ernst Kuschke2-Mar-04 19:40 
GeneralFinalizers should almost never be written [modified] Pin
Frank Hileman2-Mar-04 3:28
Frank Hileman2-Mar-04 3:28 
GeneralRe: Finalizers should almost never be written Pin
Ernst Kuschke2-Mar-04 3:39
Ernst Kuschke2-Mar-04 3:39 
GeneralRe: Finalizers should almost never be written Pin
kimjim14-Mar-06 19:37
kimjim14-Mar-06 19:37 
GeneralRe: Finalizers should almost never be written Pin
Frank Hileman24-Apr-07 7:00
Frank Hileman24-Apr-07 7:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.