CLR and Garbage Collection


The CLR maintains a heap, a service that provides memory for the objects and values whose lifetime is managed by the GC. Each time you construct an instance of a class with new, or you create a new array object, the CLR allocates a new heap block. The GC decides when to deallocate that block.

A heap block contains all the nonstatic fields for an object, or all the elements if it’s an array. The CLR also adds a header, which is not directly visible to your program. This includes a pointer to a structure describing the object’s type. This supports operations that depend on the real type of an object. 


For example, if you call GetType on a reference, the runtime uses this pointer to find out the type.  It’s also used to work out which method to use when you invoke a virtual method or an interface member. 

The CLR also uses this to know how large the heap block is—the header does not include the block size, because the runtime can work that out from the object’s type. (Most types are fixed size. There are only two exceptions, strings and arrays, which the CLR handles as special cases.) The header contains one other field, which is used for a variety of diverse purposes, including multithreaded synchronization and default hash code generation

On a 32-bit system, the header is 8 bytes long, and if you’re running in a 64-bit process, it takes 16 bytes

So an object that contained just one field of type double (an 8-byte type) would consume 16 bytes in a 32-bit process, and 24 bytes in a 64-bit process.


Although objects (i.e., instances of a class) always live on the heap, instances of value types are different: some live on the heap, and some don’t. The CLR stores some value-typed local variables on the stack, for example, but if the value is in an instance field of a class, the class instance will live on the heap, and that value will therefore live inside that object on the heap. And in some cases, a value will have an entire heap block to itself.

Example of Using and discarding objects
public static string WriteUrl(string relativeUri)
{
    var baseUri = new Uri("https://somewebsite.com/");
    var fullUri = new Uri(baseUri, relativeUri);
    var w = new WebClient();
    return w.DownloadString(fullUri);
}

The CLR analyzes the way in which we use local variables and method arguments. 

For example, although the relativeUri argument is in scope for the whole method, we use it just once as an argument when constructing the second Uri, and then never use it again

A variable is described as live from the first point where it receives a value up until the last point at which it is used
Method arguments are live from the start of the method until their final usage, unless they are unused, in which case they are never live

Local variables become live laterbaseUri becomes live once it has been assigned its initial value, and then ceases to be live with its final usage, which in this example, happens at the same point as relativeUri

Liveness is an important property in determining whether a particular object is still in use.

Suppose that when program reaches the line that constructs the WebClient, the CLR doesn’t have enough free memory to hold the new object. Then it has 2 options:

1. It could request more memory from the OS at this point

2. Try to free up memory from objects that are no longer in use, meaning that our program wouldn’t need to consume more memory than it’s already using and that where thing get interesting

The process of Freeing Up Memory

Determining Reachability