{"id":23157,"date":"2004-09-25T14:58:00","date_gmt":"2004-09-25T14:58:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/maoni\/2004\/09\/25\/using-gc-efficiently-part-2\/"},"modified":"2021-10-04T16:38:47","modified_gmt":"2021-10-04T23:38:47","slug":"using-gc-efficiently-part-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/using-gc-efficiently-part-2\/","title":{"rendered":"Using GC Efficiently \u2013 Part 2"},"content":{"rendered":"<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">In this article I\u2019ll 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.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><b><font size=\"2\"><font face=\"Verdana\">Existing GC flavors in the runtime today<\/font><\/font><\/b><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">We have the following flavors of GC today:<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><span><span><font face=\"Verdana\" size=\"2\">1)<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Verdana\" size=\"2\">Workstation GC with Concurrent GC off<\/font><\/p>\n<p class=\"MsoNormal\"><span><span><font face=\"Verdana\" size=\"2\">2)<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Verdana\" size=\"2\">Workstation GC with Concurrent GC on<\/font><\/p>\n<p class=\"MsoNormal\"><span><span><font face=\"Verdana\" size=\"2\">3)<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Verdana\" size=\"2\">Server GC<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">If you are writing a standalone managed application and don\u2019t 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\u2019t exactly mention much about concurrent GC and sometimes refers to it as the \u201cbackground GC\u201d (while refering Workstation GC as \u201cforeground GC\u201d).<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">If your application is hosted, the host might change the GC flavor for you.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">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.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><b><font size=\"2\"><font face=\"Verdana\">Design goals<\/font><\/font><\/b><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">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.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">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.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">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.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><b><font size=\"2\"><font face=\"Verdana\">How they work<\/font><\/font><\/b><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">Let\u2019s start with <b><i>Workstation GC with Concurrent GC off<\/i><\/b>. The flow goes like this:<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><span><span><font face=\"Verdana\" size=\"2\">1)<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Verdana\" size=\"2\">A managed thread is doing allocations;<\/font><\/p>\n<p class=\"MsoNormal\"><span><span><font face=\"Verdana\" size=\"2\">2)<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Verdana\" size=\"2\">It runs out of allocations (and I\u2019ll explain what this means);<\/font><\/p>\n<p class=\"MsoNormal\"><span><span><font face=\"Verdana\" size=\"2\">3)<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Verdana\" size=\"2\">It triggers a GC which will be running on this very thread;<\/font><\/p>\n<p class=\"MsoNormal\"><span><span><font face=\"Verdana\" size=\"2\">4)<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Verdana\" size=\"2\">GC calls SuspendEE to suspend managed threads;<\/font><\/p>\n<p class=\"MsoNormal\"><span><span><font face=\"Verdana\" size=\"2\">5)<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Verdana\" size=\"2\">GC does its work;<\/font><\/p>\n<p class=\"MsoNormal\"><span><span><font face=\"Verdana\" size=\"2\">6)<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Verdana\" size=\"2\">GC calls RestartEE to restart the managed threads;<\/font><\/p>\n<p class=\"MsoNormal\"><span><span><font face=\"Verdana\" size=\"2\">7)<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Verdana\" size=\"2\">Managed threads start running again.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">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\u2019t concerned with native threads so for example if the thread is calling out to some Win32 APIs it\u2019ll run while the GC is doing it work.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">For the generations we have this concept called a \u201cbudget\u201d. 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.<\/font><\/p>\n<p class=\"MsoNormal\"><span><font face=\"Verdana\" size=\"2\"><\/font><\/span>&nbsp;<\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">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. <\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">When <b><i>Concurrent GC<\/i><\/b> 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\u2019s in the Gen0 budget, the allocating threads will be blocked (to wait for GC to finish) if they need to allocate more.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">Note since Gen0 and Gen1 collections are very fast, it doesn\u2019t 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.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"2\"><font face=\"Verdana\"><b><i>Server GC<\/i><\/b> 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:<\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><span><span><font face=\"Verdana\" size=\"2\">1)<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Verdana\" size=\"2\">A managed thread is doing allocations;<\/font><\/p>\n<p class=\"MsoNormal\"><span><span><font face=\"Verdana\" size=\"2\">2)<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Verdana\" size=\"2\">It runs out of allocations on the heap its allocating on;<\/font><\/p>\n<p class=\"MsoNormal\"><span><span><font face=\"Verdana\" size=\"2\">3)<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Verdana\" size=\"2\">It signals an event to wake the GC threads to do a GC and waits for it to finish;<\/font><\/p>\n<p class=\"MsoNormal\"><span><span><font face=\"Verdana\" size=\"2\">4)<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Verdana\" size=\"2\">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);<\/font><\/p>\n<p class=\"MsoNormal\"><span><span><font face=\"Verdana\" size=\"2\">5)<\/font><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span><\/span><\/span><font face=\"Verdana\" size=\"2\">Managed threads start running again.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><b><font size=\"2\"><font face=\"Verdana\">Configuration<\/font><\/font><\/b><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">To turn Concurrent GC off, use <\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&lt;configuration&gt; <\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"2\"><font face=\"Verdana\"><span>&nbsp;&nbsp;&nbsp; <\/span>&lt;runtime&gt; <\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"2\"><font face=\"Verdana\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>&lt;gcConcurrent enabled=&#8221;false&#8221;\/&gt; <\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"2\"><font face=\"Verdana\"><span>&nbsp;&nbsp;&nbsp; <\/span>&lt;\/runtime&gt; <\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&lt;\/configuration&gt; <\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">in your application config file.<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">To turn Server GC on, use <\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&lt;configuration&gt; <\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"2\"><font face=\"Verdana\"><span>&nbsp;&nbsp;&nbsp; <\/span>&lt;runtime&gt; <\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"2\"><font face=\"Verdana\"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <\/span>&lt;gcServer enabled=\u201ctrue&#8221;\/&gt; <\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font size=\"2\"><font face=\"Verdana\"><span>&nbsp;&nbsp;&nbsp; <\/span>&lt;\/runtime&gt; <\/font><\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&lt;\/configuration&gt;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">&nbsp;<\/font><\/p>\n<p class=\"MsoNormal\"><font face=\"Verdana\" size=\"2\">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).<\/font><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this article I\u2019ll 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. &nbsp; Existing GC flavors in the runtime today &nbsp; We have the [&hellip;]<\/p>\n","protected":false},"author":3542,"featured_media":58792,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685],"tags":[3011,108],"class_list":["post-23157","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","tag-maoniposts","tag-performance"],"acf":[],"blog_post_summary":"<p>In this article I\u2019ll 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. &nbsp; Existing GC flavors in the runtime today &nbsp; We have the [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/23157","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/3542"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=23157"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/23157\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=23157"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=23157"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=23157"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}