Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GC.Collect on only generation 2 & large object heap

In my application there is a specific time when a number of large objects are all released at once. At that time I would like to do a garbage collection on specifically the large object heap (LOH).

I'm aware that you cannot do that, you must call GC.Collect(2) because the GC is only invoked on the LOH when it is doing a generation 2 collection. However, I've read in the documentation that calling GC.Collect(2) would still run a GC on generations 1 and 0.

Is it possible to force the GC to only collect gen 2, and not include gen 1 or gen 0?

If it is not possible, is there a reason for the GC to be designed that way?

like image 748
DevinB Avatar asked Sep 23 '09 22:09

DevinB


2 Answers

It's not possible. The GC is designed so that a generation 2 collection always also collects generation 0 and 1.

Edit: Found you a source for this on a GC developer's blog:

Gen2 GC requires a full collection (Gen0, Gen1, Gen2 and LOH! Large objects are GC’ed at every Gen2 GC even when the GC was not triggered by lack of space in LOH. Note that there isn’t a GC that only collects large objects.) which takes much longer than younger generation collections.

Edit 2: From the same blog's Using GC Efficiently Part 1 and Part 2 apparently Gen0 and Gen1 collections are fast compared to a Gen2 collection, so that it seems reasonable to me that only doing Gen2 wouldn't be of much performance benefit. There might be a more fundamental reason, but I'm not sure. Maybe the answer is in some article on that blog.

like image 159
Joren Avatar answered Oct 18 '22 21:10

Joren


Since all new allocations (other than for large objects) always go in Gen0, the GC is designed to always collect from the specified generation and below. When you call GC.Collect(2), you are telling the GC to collect from Gen0, Gen1, and Gen2.

If you are certain you are dealing with a lot of large objects (objects that at allocation time are large enough to be placed on the LOH) the best option is to ensure that you set them to null (Nothing in VB) when you are done with them. LOH allocation attempts to be smart and reuse blocks. For example, if you allocated a 1MB object on the LOH and then disposed of it and set it to null, you would be left with a 1MB "hole". The next time you allocate anything on the LOH that is 1MB or smaller in size, it will fill in that hole (and keep filling it in until the next allocation is too large to fit in the remaining space, at which point it will allocate a new block.)

Keep in mind that generations in .NET are not physical things, but are logical separations to help increase GC performance. Since all new allocations go in Gen0, that is always the first generation to be collected. Each collection cycle that runs, anything in a lower generation that survives collection is "promoted" to the next highest generation (until in reaches Gen2).

In most cases, the GC doesn't need to go beyond collecting Gen0. The current implementation of the GC is able to collect Gen0 and Gen1 at the same time, but it can't collect Gen2 while Gen0 or Gen1 are being collected. (.NET 4.0 relaxes this constraint a great deal and for the most part, the GC is able to collect Gen2 while Gen0 or Gen1 are also being collected.)

like image 26
Scott Dorman Avatar answered Oct 18 '22 21:10

Scott Dorman