2005-04-13
| Table of Contents: |
| Rate This Article: | Add This Article To: |
( Page 2 of 5 )
A primary task that faces every software developer is allocating and managing resources. Resources are always finite: a computer has a limited amount of RAM, the operating system has a limit on number of file handles, the network throughput is limited, and so on. Instead of engaging the warp speed of their pure creativity, developers have to spend their time dealing with mundane details, muttering things like, "I need to release this file handle, otherwise Win32 is going to be unhappy about things real soon...".
It only becomes worse in the object-oriented world. Every type identifies some resource available for a program's use. Create an object of that type, and you end up using some of that resource.
Let's consider the typical steps required to access a resource:
- Allocate memory for the type that represents the resource.
- Initialize the memory to set the initial state of the resource and make it useable.
- Use the resource, by accessing the instance variables (this might be repeated more than once).
- Tear down the state of the resource to clean up.
- Free memory that was allocated for that resource.
The only meaningful steps are 2 and 3, resource initialization and use. The rest of the stuff is maintenance steps to make sure that your program is a good citizen and that it cleans up after itself by returning resources to the system. Wouldn't it be nice to be able to get rid of the (manual) implementation of those steps? .NET does just that with the garbage collection and finalization mechanisms.
The Microsoft .NET Common Language Runtime (CLR) requires that all resources be allocated from the managed heap. After resources are allocated in that special way, you never have to free them; objects are automatically freed when they are no longer needed by an application. A special mechanism, called garbage collection (GC) is responsible for freeing objects that are not longer used by an application. Microsoft .NET's garbage collector optimizing engine determines the best time to perform a collection, based upon the allocation being made. When the garbage collector performs a collection, it checks for objects in the managed heap that are no longer being used by the application and performs the necessary operations to reclaim their memory.
Garbage Collection Mechanism
In order to perform the memory management, garbage collection has to know the location of the roots. Roots are then used to determine when an object is no longer in use by an application. In addition, each data type in .NET has associated metadata that describes that type. Metadata describes the layout of object in memory and its size, which help the garbage collector in the compaction stage of collection.
We mentioned roots earlier. Every application has a set of roots. Roots are used to identify storage locations; a list of active roots is maintained by the just-in-time (JIT) compiler and common language runtime, and is made accessible to the garbage collection algorithm. Application roots typically include:
- All global and static pointers in an application
- Local variable/parameters object pointers on an application thread's stack
- Any CPU registers containing pointers to objects in the managed heap
- Pointers to objects found in
ReadyToFinalizequeue (we'll discuss this in more detail later).
As the list shows, application roots are pointers contained outside of other objects. For example: static or global variables, a thread's stack content, CPU registers. They are indeed "roots," since you can trace the whole tree starting from them. Let's look how they are used during the garbage collection pass.
When garbage collection is running, the assumption is that all objects in the managed heap are garbage; i.e. none of the application roots refer to any projects in the heap. At this point, the garbage collection engine starts recursively mapping all objects that can be reached from the first application root and marks them reachable. Upon completion of this segment, the garbage collection engine repeats the same process with the next application root, until all application roots are processed. Once all roots are processed, all objects that are not marked reachable are considered not used ("garbage"); then, the cleanup process starts a linear search of continuous blocks of garbage objects.
During the linear search, memory is compacted, i.e. the non-garbage objects are shifted in memory compressing gaps in the managed heap. Because object pointers are now invalidated, the garbage collector must modify the application roots so that pointers point to the object's new location. Now, when unused objects are removed and memory is compacted, the managed heap can continue allocating memory for new objects.
It looks like we have everything covered. Memory is nicely reclaimed back to the managed heap, and can be re-used to create new objects again. But what about the outside resources your application uses, such as files, database and network connections, and graphic device contexts? They are not directly related to the objects, and it would be nice to be able to properly dispose of them when in-memory objects are garbage collected. Microsoft .NET provides a convenient way to do it by providing the Finalize() and Dispose() mechanisms as part of garbage collection.
![]() |
|


