The CLR requires that all resources to be allocated from a heap called the managed heap. This heap is similar to a C-runtime heap, except that you never delete objects from the managed heap—objects are automatically deleted when the application no longer needs them. This, of course, raises the question, "How does the managed heap know when the application is no longer using an object?" I’ll address this question shortly. Several garbage-collection algorithms are in use today. Each algorithm is fine-tuned for a particular environment to provide the best performance. In this chapter, I’ll concentrate on the garbage-collection algorithm used by the Microsoft .NET Framework’s CLR. Let’s start off with the basic concepts.When a process is initialized, the CLR reserves a contiguous region of address space that initially contains no backing storage. This address space region is the managed heap. The heap also maintains a pointer, which I’ll call NextObjPtr. This pointer indicates where the next object is to be allocated within the heap. Initially, NextObjPtr is set to the base address of the reserved address space region.The newobj intermediate language (IL) instruction creates an object. Many languages (including C#, C++/CLI, and Microsoft Visual Basic) offer a new operator that causes the compiler to emit a newobj instruction into the method’s IL code. The newobj instruction causes the CLR to perform the following steps:
- Calculate the number of bytes required for the type’s (and all of its base type’s) fields.
- Add the bytes required for an object’s overhead. Each object has two overhead fields: fields requires 32 bits, adding 8 bytes to each object. For a 64-bit application, each field is 64 bits, adding 16 bytes to each object. The CLR then checks that the bytes required to allocate the object are available in the reserved region (committing storage if necessary). If there is enough free space in the managed heap, the object will fit, starting at the address pointed to by NextObjPtr, and these bytes are zeroed out. The type’s constructor is called (passing NextObjPtr for the this parameter), and the newobj IL instruction (or C#’s new operator) returns the address of the object. Just before the address is returned, NextObjPtr is advanced past the object and now points to the address where the next object will be placed in the heap.
Figure shows a managed heap consisting of three objects: A, B, and C. If a new object were to be allocated, it would be placed where NextObjPtr points to (immediately after object C).
The managed heap gains these advantages because it makes one really big assumption: that address space and storage are infinite. Obviously, this assumption NextObjPtr is ridiculous, and the managed heap must employ a mechanism to allow it to make this assumption. This mechanism is the garbage collector. Here’s how it works: When an application calls the new operator to create an object, there might not be enough address space left in the region to allocate to the object. The heap detects this lack of space by adding the bytes that the object requires to the address in NextObjPtr. If the resulting value is beyond the end of the address space region, the heap is full, and a garbage collection must be performed.