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
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 later;
baseUri
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 point2. 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