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.)