In an unmanaged runtime, such as C or C++, you allocate and free up memory for your application through code. A managed runtime, on the other hand, typically has a process governor that manages memory for your application so that you don’t have to. The mechanism that does this is usually referred to as the garbage collector. The .NET runtime has a configurable garbage collector that usually does just fine without software developers caring very much about it. Normally, it just works and does a very good job of managing memory for your app. It can be important, however, to understand how the garbage collector works at times, even if you never need to change the way it operates.
.NET Garbage Collection Basics
The garbage collector is invoked by the runtime to clean up unused memory and defragment the heap generally whenever the application feels pressure to do so. While you can request that garbage collection happen at specific points in your app, you cannot explicitly start garbage collection yourself. And, that being said, it is generally frowned upon to make calls to GC.Collect().
Memory on the heap is divided up into sections, called generations, that roughly refer to the length of time that an object has been allocated and its size. There are three generations: generation 0 holds objects that have been allocated since the last GC, generation 1 holds objects that have survived generation 0, and generation 2 holds objects that have survived generation 1. Generation 2 is somewhat special in that objects greater than 85,000 bytes go straight to generation 2. This is why generation 2 is sometimes referred to as the large-object heap. Because large objects are expensive to move and compact, they go straight to gen 2 so that the GC doesn’t waste time compacting them or moving them from generation to generation. When a small object is allocated, it goes into generation 0. If the GC runs and that object is still in use, it moves to generation 1. Similarly, if the object remains in use when a GC is performed on generation 1, then it moves to generation 2. If at any point the GC finds that a gen 0 or gen 1 object is no longer referenced, the object is cleaned out of the heap and the resulting space is compacted along with all of the other remaining object to avoid heap fragmentation. It is important to be aware that when objects are removed from the heap, it can leave “holes” in the memory space, making it difficult for the runtime to find a space for newly-allocated objects. The compaction that happens during a GC repacks the memory in the heap to make new allocations more efficient.
Foreground and Background Collection
Background collection has slowly been introduced as a replacement for the older “concurrent” collection mechanism. As of Framework 4.5, all garbage collection modes operate with background collection enabled by default. With background collection, a dedicated thread is given for the GC in which it performs gen 2 maintenance asynchronously with the application threads. Generation 0 and generation 1 collection is still performed normally in what’s called foreground collection, but this can happen somewhat in parallel with the generation 2 maintenance happening on the dedicated GC thread. Background collection allows for fewer app interruptions since the foreground collection can happen in parallel and the expensive gen 2 maintenance happens out-of-band.
.NET Garbage Collection Modes
There are two types of .NET garbage collection modes, workstation and server. Prior to .NET Framework 4.5, the two modes had wildly different behaviors. With Framework 4.5, the two modes at least operate the same way but with different threading concerns. As such, I won’t go into detail on the history of the GC for apps prior to Framework 4.5, but I will attempt to explain how the GC works and when you want workstation versus server GC mode.
Workstation garbage collection is the default and the only one available for single-processor machines. That being said, ASP.NET and SQL Server-hosted applications turn on server garbage collection mode by default. With workstation mode, the garbage collector operates on the same thread(s) as your application. When collection is necessary, your application thread is suspended and the garbage collector does its work and yields the thread back to the application. Since the GC runs on the same thread as your application, the collection runs with the same thread priority as your application. It must compete for time slices in the scheduler just like your application.
Server garbage collection can be enabled in the application configuration as shown below and has several benefits for large scale server applications. In server mode, both the background and foreground GC threads are dedicated threads. While your application threads are still paused during foreground GC, the garbage collector is given a dedicated thread for each logical processor. These dedicated GC threads run in the highest OS priority so the GC does not have to compete as heavily with application threads.
How to Choose
Microsoft cautions that server garbage collection can cause performance issues under extremely high memory pressures. Since the GC threads run at higher priority than the application, the GC activities can starve out the app threads if the GC is extremely active due to memory pressure. With that said, the basic idea seems to be that if your app is healthy, multi-threaded and taking advantage of multiple processors, then it could benefit from server garbage collection mode. Since each processor core has a dedicated garbage collection thread, the GC is more capable of doing its work to ensure that app threads on each core are being taken care of. This increases the overall throughput of a multi-threaded app (potentially at the cost of individual app threads if they require a lot of maintenance). GC mode is enabled by default if your application is running ASP.NET or hosted within SQL Server then server. In contrast, if your application is generally single-threaded or is not hosted as a service requiring high-throughput, then workstation mode can provide better end-user responsiveness.