Search content within the blog

Sunday, April 10, 2011

Garbage collector in C#

The .NET garbage collector is known as generational garbage collector. The objects allocated are categorized into three generations. Most recently allocated objects are placed in generation 0.
Objects in generation 0, that survive a garbage collection pass are moved to generation 1.
generation 2 contains long-lived objects, that survive after the two collection passes.

A garbage collection pass for generation 0 is the most common type of collection. Generation 1 collection pass is performed if generation 0 collection pass is not sufficient to reclaim memory.
Atlast, generation 2 collection pass is peformed if collection pass on generation 0 and 1 are not sufficient to reclaim memory. If no memory is available, after all the collection passes, an
OutOfMemoryException is thrown.

Dispose
public void Dispose()
{
// all clean up source code here..
GC.SuppressFinalize(this);
}

It is advisable to place these calls in a finally clause, which guarantees that the objects are properly handled even if an exception is thrown.

When you exit the program, you can make the System.GC methods force the GC to schedule the implicit finalizer call, and then wait for that call to complete, as shown.

System.GC.Collect();
System.GC.WaitForPendingFinalizers();

You can also call System.GC.SuppressFinalize to prevent the garbage collector from implicitly invoking the finalizer a second time.

Do not directly call your base class Finalize method. It is called automatically from your destructor.

Destructors and object.Finalize cannot be called directly. Consider calling IDisposable.Dispose, if available.

To explicitly destroy the object, you should call System.GC.SuppressFinalize immediately to prevent multiple object deallocation by the GC:

System.GC.SuppressFinalize(anyInstance);

// example: explicit destruction/finalize
using System;

class MyClass : IDisposable
{
public MyClass() //default ctor
{
this.iNumber = 0;
System.Console.WriteLine("ctor:MyClass {0}", iNumber);
}

public MyClass(Int32 iNumber) // specialized ctor
{
this.iNumber = iNumber;
System.Console.WriteLine("ctor:MyClass {0}", iNumber);
}

~MyClass() // dtor or finalize
{
System.Console.WriteLine("dtor:~MyClass {0}", iNumber);
}

public void Dispose() // helper finalize function
{
// here you can free the resources you allocated explicitly
System.GC.SuppressFinalize(this);
}

private int iNumber;
}

class main
{
static void Main()
{
MyClass myClass1 = new MyClass();
MyClass myClass2 = new MyClass(19);
myClass1.Dispose(); // myClass1 is explicitly exposed.
System.GC.Collect();
System.GC.WaitForPendingFinalizers();

// myClass2 is implicitly exposed by GC.
Console.ReadLine();
}
}



using System;

namespace GCCollect
{
class Account
{
public Account(string accountNumber)
{
this.accountNumber = accountNumber;
Console.WriteLine("Account::Acount - c'tor");
}
~Account()
{
Console.WriteLine("Account::~Acount - d'tor");
}

protected string accountNumber;
override public string ToString() { return accountNumber; }
};

class Class1
{
[STAThread]
static void Main(string[] args)
{
CreateAccount("111006116");

GC.Collect();
GC.WaitForPendingFinalizers();

Console.WriteLine("Application ending");
}

public static void CreateAccount(string accountNumber)
{
Console.WriteLine("CreateAccount - instantiate Account object");
Account account = new Account(accountNumber);
Console.WriteLine("CreateAccount - created account number {0}",
account);
}
}
}


Note a couple of issues here:

1. I allocated the Account object in a different method than the Main method. This is because if I allocated the Account object in the Main method and then called GC.Collect in the same method, the Account object would technically still be associated with running code and would therefore not be eligible for collection.
2. While I used a simple single object example here to illustrate calling the GC.Collect method, the GC.Collect method is designed not to control specific object destruction, but to allow for the forcing of collection for all of unused objects. Therefore, it's a very expensive operation and should only be used in cases where you want/need to force global collection.
3. For situations where you want to force the finalization of a specific object, you should implement the Dispose pattern, which I'll cover in a separate article.

No comments:

Post a Comment