C# depends on the CLR for many of its runtime services, and garbage collection is no exception.
The CLR includes a high-performing generational mark-and-compact garbage collector (GC) that performs automatic memory management for type instances stored on the managed heap.
The GC is considered to be a tracing garbage collector in that it doesn't interfere with every access to an object, but rather wakes up intermittently and traces the graph of objects stored on the managed heap to determine which objects can be considered garbage and therefore collected.
The GC generally initiates a garbage collection when a memory allocation occurs and memory is too low to fulfill the request. This process can also be initiated manually using the System.GC type. Initiating a garbage collection freezes all threads in the process to allow the GC time to examine the managed heap.
The GC begins with the set of object references considered roots, and walks the object graph, marking all the objects it touches as reachable. Once this process is complete, all objects that have not been marked are considered to be garbage.
Objects that are considered garbage and don't have finalizers are immediately discarded, and the memory is reclaimed. Objects that are considered garbage and do have finalizers are flagged for additional asynchronous processing on a separate thread to invoke their Finalize methods before they can be considered garbage and reclaimed at the next collection.
Objects considered still live are then shifted down to the bottom of the heap (compacted), hopefully freeing space to allow the memory allocation to succeed.
At this point the memory allocation is attempted again, the threads in the process are unfrozen, and either normal processing continues or an OutOfMemoryException is thrown.