9

During the day I see a lot of gen 2 collections in our windows service.

When does GC decide to do full collection instead of collecting only Gen1 and Gen0 or only Gen0?

Piotr Perak
  • 10,718
  • 9
  • 49
  • 86

2 Answers2

10

Read http://msdn.microsoft.com/en-us/library/0xy59wtx.aspx and the linked articles for more information. In general, gen2 is collected "when necessary."

One thing that can cause excessive Gen2 collections is the large object heap (LOH). When the LOH gets filled, it will trigger a full collection. If your application allocates and frees lots of large objects (80K or larger), that could very well be your problem.

See CLR Inside Out: Large Object Heap Uncovered.

Also consider using the server garbage collector in your service. It will typically provide better performance (fewer collections). Add this to your app.config file:

<configuration
   <runtime>
      <gcServer enabled="true"/>
   </runtime>
</configuration> 
Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • Our service checks if there is work to do and queues it in ThreadPool. I think that those ThreadPool Threads are the only ones that go up to Gen2. We don't allocate large objects. But I see Gen2 collections when system memory is low. AFAIK server mode is default when you use multicore CPU. – Piotr Perak Dec 20 '11 at 21:38
  • @Peri If you system memory is low it will trigger a full GC. The system needs the space back!. You might want to look at the possibility of adding more RAM. –  Dec 20 '11 at 21:52
  • 2
    @Peri: No, server mode is not the default. The concurrent GC is the default when you use multicore, but that's still the client GC. If you want the server GC, you must say so in your app.config file. Also, are you sure that you don't allocate large objects? On a 64-bit system, a list with only 10,000 items in it will require a backing array larger than 80 kilobytes. – Jim Mischel Dec 21 '11 at 13:27
  • I think I was thinking about ASP.NET apps. In ASP.NET it is server mode by default. – Piotr Perak Dec 21 '11 at 14:17
7

Generally a colletion is triggered (under any generation) when 1 of the following is met

  • Allocation Exceeds the Threshold. I vaugly remember this being dynamic based on the current needs. Can anyone confirm/deny?
  • There is a Low Memory Situation
  • It is called via GC.Collect()

There may be another scenario I am missing, but generally that is it. The first one is what usually kicks off a collection, and conisdering it is (relativly) more expensive to collect G2 than G0 you see less of them.


To address the question in the comment:
Each generation has a threshold that when hit will trigger a collection. Gen0 could be 5mb, and will be triggered when that is filled. After GC runs, if you still have 5mb in there, I believe it will increase the limit. If it didn't than every allocation will trigger a collection, and you have an issue. Gen2 could be 20mb (note I am making up numbers here) and the same logic applies there.

For a textbook example, let's look at a simple scenario.

  1. New app allocated objects and all are put at Gen0 for a total of 3MB of data. (this is not always the case, but pretend it is)
  2. A GC hits, and 1 MB of that is moved to G1, the rest is cleaned up.
  3. G0 is now free, and G1 has 1MB in it.
  4. More objects are created, and more GC's happen. After a while G1 gets full, and some are moved to G2.
  5. Objects stay there until G2 is full, and then they are cleaned if not in use. If an object is still being referenced, it will stay in G2 until it can be cleaned.

A full GC is expensive, and I have seen days go by without one happening. Granted this was on a system with >64GB of ram available, and there was no need for it.

  • What do you mean in first point? That for example when I allocate <5MB it only collects Gen0 (on each allocate?), Gen1 when I allocate 5-10MB and Gen2 when I allocate more then 10MB? – Piotr Perak Dec 20 '11 at 21:34
  • I'm not sure I understand the example. I think objects are moved to older generations when they survive GC (are still live) not when generation is 'full'. – Piotr Perak Dec 20 '11 at 22:20
  • right, when a GC happens is the key, and being "full" will kick it off (or one of the other 2 reasons laid out above). I was just using that as an example. –  Dec 20 '11 at 22:37
  • The GC algorithm doesn't seem to take into account an excess of system memory, even when it's supposed to i.e. running under SustainedLowLatency mode. – Chuu Aug 29 '14 at 18:30
  • Also, another important criteria that triggers a GC is when the ephemeral segment needs to grow, either because it's completely out of space or because of heap fragmentation. This is particularly annoying if you're dealing with busty data and a latency SLA target because your GC events will be highly correlated with data bursts -- exactly when you don't want a GC to occur. – Chuu Aug 29 '14 at 18:31