Using GC Efficiently – Part 2

In this article I’ll talk about different flavors of GC, the design goals behind each of them and how they work differently from each other so you can make a good decision of which flavor of GC you should choose for your applications.


Existing GC flavors in the runtime today


We have the following flavors of GC today:


1)      Workstation GC with Concurrent GC off

2)      Workstation GC with Concurrent GC on

3)      Server GC


If you are writing a standalone managed application and don’t do any config on your own, you get 2) by default. This might come as a surprise to a lot of people because our document doesn’t exactly mention much about concurrent GC and sometimes refers to it as the “background GC” (while refering Workstation GC as “foreground GC”).


If your application is hosted, the host might change the GC flavor for you.


One thing worth mentioning is if you ask for Server GC and the application is running on a UP machine, you will actually get 1) because Workstation GC is optimized for high throughput on UP machines.


Design goals


Flavor 1) is designed to achieve high throughput on UP machines. We use dynamic tuning in our GC to observe the allocation and surviving patterns so we adjust the tuning as the program runs to make each GC as productive as possible.


Flavor 2) is designed for interactive applications where the response time is critical. Concurrent GC allows for shorter pause time. And it trades some memory and CPU to achieve this goal so you will get a slightly bigger working set and slightly longer collection time.


Flavor 3), as the name suggests, is designed for server applications where the typical scenario is you have a pool of worker threads that are all doing similar things. For example, handling the same type of requests or doing the same type of transactions. All these threads tend to have the same allocation patterns. The server GC is designed to have high throughput and high scalibility on multiproc machines.


How they work


Let’s start with Workstation GC with Concurrent GC off. The flow goes like this:


1)      A managed thread is doing allocations;

2)      It runs out of allocations (and I’ll explain what this means);

3)      It triggers a GC which will be running on this very thread;

4)      GC calls SuspendEE to suspend managed threads;

5)      GC does its work;

6)      GC calls RestartEE to restart the managed threads;

7)      Managed threads start running again.


In step 5) you will see that all managed threads are stopped waiting for GC to complete if you break into the debugger at that time. SuspendEE isn’t concerned with native threads so for example if the thread is calling out to some Win32 APIs it’ll run while the GC is doing it work.


For the generations we have this concept called a “budget”. Each generation has its own budget which is dynamically adjusted. Since we always allocate in Gen0, you can think of the Gen0 budget as an allocation limit that when exceeded, a GC will be triggered. This budget is completely different from the GC heap segment size and is a lot smaller than the segment size.


The CLR GC can do either compacting or non compacting collections. Non compacting, also called sweeping, is cheaper than compacting that involves copying memory which is an expensive operation.


When Concurrent GC is on, the biggest difference is with Suspend/Restart. As I mentioned Concurrent GC allows for shorter pause time because the application need to be responsive. So instead of not letting the managed threads run for the duration of the GC, Concurrent GC only suspends these threads for a very short period of times a few times during the GC. The rest of the time, the managed threads are running and allocating if they need to. We start with a much bigger budget for Gen0 in the Concurrent GC case so we have enough room for the application to allocate while GC is running. However, if during the Concurrent GC the application already allocated what’s in the Gen0 budget, the allocating threads will be blocked (to wait for GC to finish) if they need to allocate more.


Note since Gen0 and Gen1 collections are very fast, it doesn’t make sense to have Concurrent GC when we are doing Gen0 and Gen1 collections. So we only consider doing a Concurrent GC if we need to do a Gen2 collection. If we decide to do a Gen2 collection, we will then decide if this Gen2 collection should be concurrent or non concurrent.


Server GC is a totally different story. We create a GC thread and a separated heap for each CPU. GC happens on these threads instead of on the allocating thread. The flow looks like this:


1)      A managed thread is doing allocations;

2)      It runs out of allocations on the heap its allocating on;

3)      It signals an event to wake the GC threads to do a GC and waits for it to finish;

4)      GC threads run, finish with the GC and signal an event that says GC is complete (When GC is in progress, all managed threads are suspended just like in Workstation GC);

5)      Managed threads start running again.




To turn Concurrent GC off, use




        <gcConcurrent enabled=”false”/>




in your application config file.


To turn Server GC on, use




        <gcServer enabled=“true”/>




in your application config file, if you are using Everett SP1 or Whidbey. Before Everett SP1 the only supported way is via hosting APIs (look at CorBindToRuntimeEx).

Comments (66)

  1. Hi,

    Will concurrent GC block all managed threads during compact phase?

  2. Question: How many threads does a typical managed process have when it just starts to run?&amp;nbsp;&amp;nbsp;…

  3. Question: How many threads does a typical managed process have when it just starts to run?&amp;nbsp;&amp;nbsp;…

  4. Question: How many threads does a typical managed process have when it just starts to run?&amp;nbsp;&amp;nbsp;…

  5. I previously blogged about a set must-read garbage collection articles&amp;nbsp;and issues around directly…

  6. I’m often asked &quot;What’s new in Whidbey&quot; and so I thought I’d put together this (very) brief list of some…

  7. I’m often asked &quot;What’s new in Whidbey&quot; and so I thought I’d put together this (very) brief list of some…

  8. I’m often asked &quot;What’s new in Whidbey&quot; and so I thought I’d put together this (very) brief list of some…

  9. Using GC Efficiently – Part 1

    Using GC Efficiently – Part 2

    Using GC Efficiently – Part 3


  10. Question: How many threads does a typical managed process have when it just starts to run?&amp;nbsp;&amp;nbsp;…

  11. In Using GC Efficiently – Part 2 I talked about different flavors of GC that exist in the CLR and how…

  12. Spending lots of time on C++ means I haven’t been paying as much attention to managed code as I did in…

  13. Using GC

    Efficiently Part 1Maoni explains the cost of things so you can make good

    decisions in…

  14. Very many thanks for a good work. Nice and useful. Like it!

  15. In Using GC Efficiently – Part 2 I talked about different flavors of GC that exist in the CLR and how

  16. Certainly that’s one of the most frequently asked questions I get (at the PDC too!). So since PDC already

  17. Romanson says:

    Hallo! Ich habe die Seite vorhin gerade entdeckt und muss dir wirklich ein Kompliment aussprechen!

  18. chew says:

    Hi Maoni,

    Do you think the used GC in .Net has robbed its suitability in high performance computing applications, considering the fact that GC will suspend all managed threads and perform compacting of memory?


  19. maoni says:

    chew, no. Why do you think that just because GC suspends threads and compact memory?

  20. As 64-bit machines become more common, the problems we need to solve also evolve. In this post I’d like

  21. chew says:

    Hi maoni,

    Correct me if I’m wrong. I’m also hoping .Net is the choice for high performance computing. Unfortunately, in the programming forums I visited thus far, most people think that .Net is not the suitable for high performance computing. Unmanaged code is still the preferred choice for high performance computing.

    If you think GC is not the hindering factor, are you trying to say there are other factors in .Net that made it not suitable? Or do you agree .Net is definitely suitable for high performance computing? I’m been visiting forums and reading papers, hoping to get a convicing answer but to no avail so far. Appreciate if you could help out.


  22. Vse promolchat nikto ne skazhe. Vartan Teige.

  23. roy ashbrook says:

    Ah. Garbage Collection… how I love and hate thee. =P I think one sad thing about programming in .net

  24. roy ashbrook says:

    Ah. Garbage Collection… how I love and hate thee. =P I think one sad thing about programming in .net

  25. There were a lot of question on good reference material for .NET (books and online). Here is a consolidated

  26. Mo says:

    Hi Maoni,

    Which one would be best suited for SharePoint environment. Afterall MOSS 2007 is .Net 2.0 application only. So would it be helpful to put the ServerGC setting or Concurrent GC On in web.config file? Please advise.



  27. maoni says:

    Mo, can you elaborate what kind of environment "SharePoint environment" is?

  28. Deepan says:

    I have a basic question … If two .net applications are running then each will have its own GC heap, stack etc. just like any windows process …. Is this understanding correct?

    Now will there be separate GC thread for each .net application. If this is not the case and there is a single thread handling GC of all running applications, then does that imply that GC is triggered at the same time for all the applications.

    Please throw some more light on this. It would be nice if you can refer some article that explains the GC considering that multiple managed applications are running together.

  29. maoni says:

    Deepan, yes each process will have its own GC heap and stacks.

    GC is per process, which means the GC heap from one process is separated from another process; and if you are server GC for 2 managed processes they would each have their own server GC threads; and since it’s per process, GCs from different processes are triggered on each process’s own schedule.

    GC is sensitive to the memory pressure on the *machine*.

  30. Deepan says:

    Thanks for the info…. just wanted to clarify my understanding further … i think irrespective of the type of GC (Server/workstation), each .net application will have its own process space where a separate GC thread will take care of garbage collection for that process.

  31. maoni says:

    Deepan, yes each .net app will have its own process address space and GC works per process. There isn’t necessarily a separated GC thread since GC could happen on the user thread that triggered the GC.

  32. Wdyyyhoc says:

    Open this post and read what I think about that:,

  33. langolier says:

    Hello all,

    I have a big dictionary contains lots of data.

    What I do is set the reference to this same dictionary as null and then calling the Collect() method, but it doesnt release any memory. On the other hand, when I previously call the Clear() method on the dictionary, the memory is released (on calling GC.Collect(), Clear() by itself doesn’t do the trick). On the third hand 🙂 If I enclose the GC.Collect() call inside an infinite loop, calling it many times the memory gets freed on the very first time(!!) the GC.Collect() is called! But, if I place a finite loop, no mather how big, the memory is never released 🙁 Even if I place an infinite loop not obviously infinite to the compiler (something like for(int nX = 0;nX > -1;++nX) GC.Collect(); ) the memory doesn’t get released. Can anyone shed some light on this ? Maoni, could you please state the rules that lead to this odd behavior of the GC.Collect() method…? I have no finalizers. I’ve lost days trying to figure out  the exact mechanism, because I have some more or less serious problems with memory usage. Any help deeply appreciated



  34. GC flavors are a static performance optimization for the .NET garbage collector.&#160; Under various

  35. maoni says:

    Langolier, you can set a breakpoint on the GC.Collect method and do a !gcroot sos command on the object you are interested in see who has references to it. When you call GC.Collect() GC doesn’t participate in determining who’s live and who’s dead – other CLR components, like the stackwalker, determine the liveness of objects.

  36. Sudeep says:


    How can I find out if I have concurrent GC or non-concurrent GC for an app hosted in an IIS process – w3wp.exe running on a UP box? !eeversion from SOS only indicates if its server or workstation mode.


  37. Note : This entry was originally posted on 9/14/2008 5:16:11 PM. I present at a lot of the local Florida

  38. An ancient article on using the .NET garbage collector. Using GC Efficiently – Part 2…

  39. daveblack says:

    Is there a way to determine, either programmatically or not, which version of the GC is being used?

  40. daveblack says:

    I just found the System.Runtime.GCSettings.IsServerGC property to programmatically tell what mode the GC is in.

    However, my real question is…what decides the mode that the GC should run in?  I was always under the impression that the .NET Framework running on a "server OS" would use "Server GC" and any non-server OS would use the Workstation GC.  I see that the default setting for "gcServer" is disabled.

    Do I need to explicitly set the <gcServer enabled=“true"/> for .NET 3.5 on Windows Server 2003 R2 SP2 if I want to use Server GC?

  41. malathywill says:

    Is there any way to know about the GC mode for the application running currently other than System.Runtime.GCSettings.IsServerGC? Because, once I am using a desktop application in a single processor and enabled ServerGC. It gives me the result that System.Runtime.GCSettings.IsServerGC is true. We all know that even when server GC is enabled in single processor, by default workstation GC will be running in the background. How can I confirm that workstation GC is in use even when server GC is enabled?

  42. 생활지혜 says:

    진짜와 가짜 꿀을 구별하는 방법.

    가짜 꿀은 숟가락에 담아 떨어뜨렸을 때 물엿처럼 흘러

    꿀을 숟가락에 떠서 아래로 떨어뜨려 보면 된다.

    이 때 물엿처럼 주르르 흘러내리면 가짜이고,

    응축력이 있어 또박또박 잘려서 떨어지면 진짜가 틀림없다.

    그리고 꿀을 살 때는 봄에 따낸 첫 꿀과 가을에 따낸 것은 피하는 것이 좋다.

    출처:다음카페 생활의지혜!


  43. JACKY Shen says:


    what's the UP means for "UP machines"?