In the .NET world, the GC is the component that manages memory. Every time you use the new keyword to allocate a new object, it is the GC that allocates memory from the managed heap. The GC also is responsible for efficiently freeing memory from the managed heap. This action is based on allocation needs and which objects are ready for removal from the managed heap.
The function of the GC frees the developer from needing to manage memory, which is great. However, when you use unmanaged components—such as a file, window, network, and synchronization objects—you must provide a means for the GC to clean up these items. This process is done by writing a finalizer method ( ~ClassName ), which cleans up the objects for you by releasing or closing them. By default, this method does nothing and is automatically generated for you, similar to how a default destructor is handled in C++. If you do need to write your own finalizer method, you must override the existing one and write code that cleans up your unmanaged resources. The GC will then call this method when it becomes time to release the object or remove it from the heap.
The .NET runtime does provide a class that will allow you to manage the GC directly, but it is highly recommended that you avoid this if possible. The GC class provides this functionality for you. In the GC class, you can call the Collect method to force the GC to clean up immediately and call objects' finalizer methods.
| Caution |
It is recommended to call the GC Collect method only when there is a significant amount of memory that has recently been freed and needs to be restored immediately to active use for new allocations. It is very important that you use the Collect method sparingly, because overuse of the method could cause significant performance problems. |
By default, the GC is run on specific generations of the managed heap at specific thresholds, as follows:
Objects move through generations from generation 0 to generation 2 by surviving a garbage collection cycle, meaning that they remain in use and strongly rooted through a garbage collection cycle. The idea is that objects that remain in use the longest will end up in generation 2, where the garbage collection process runs the least.
Objects that exceed 85KB are placed in the large object heap. Memory in this heap is never freed. You can reuse this memory after freeing up previous objects, but it will always be present in the process. The only way to free this memory in version 1.0 of C# is to stop the process.
| Note |
The behavior of the large object heap never being compacted was changed in version 1.1 of the framework. |
You can easily determine if your memory leaks in managed or unmanaged code by using Performance Monitor. If you see Memory:Private Bytes grow and .NET Memeory:.NET # of Bytes in all Heaps remain flat, you are dealing with an unmanaged leak; otherwise, you are seeing a managed leak. You can easily use windbg with Son Of Strike (SOS) or AllocationProfiler to determine the leak.
How does the GC know when an object is ready for garbage collection? Take a look at the following code snippet:
static void Main(string[] args)
{
IClassA A;
IClassB B;
IClassC C;
IClassB XXX;
Console.WriteLine("Waiting to Debug....");
Console.ReadLine();
A = New ComObjectsLib.ClassA();
B = A.CreateB();
C = B.CreateC(); //At this point, A is considered as not being
//referenced in code again and is eligible for cleanup?
//.NET is smart enough to know the object is not used again in
//the method, and thus we really don't base lifetime on
//scope, but rather its use in a method or lack thereof.
GC.Collect();
XXX = C.DoBug();
Console.ReadLine();
}
This example illustrates when objects are available for cleanup. The problem is simple. In this code snippet, if object C had a reference to object B, and a method of object C called a method in object B, you would have an access violation (AV), because object A and B would be freed. (This same problem occurs with the WindowsIdentity class.) You can prevent this behavior by using GC.KeepAlive(object) at the end of the method.
| Note |
The example shown here is documented in the following C# specifications: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csspec/html/vclrfCSharpSpec_3_9.asp. |
The important point to remember here is that an object's lifetime is based on usage, not scope.