.NET generation 0 heap size

2019-02-08 14:30发布

问题:

Is it possible to set a minimal size of a generation 0 heap in .NET?

I have a folowing sistuation. I have a function that allocates around 20-30 MB of 1KB objects, does something with them, and terminates, leaving all the allocated objects to be GC-ed. Now, in Performance Monitor, I can see that generation 0 heap size is 5-6 MB, which is not enough to accept all the 20-30 MB of objects that I need. When I start allocating, at some point gen0 GC starts running, and since all the objects are needed it promotes them to a gen1. Next time GC starts running, these objects get promoted in gen2. So finally around 15MB of my objects end up in the gen2 heap. These are, by my logic, temporary objects that should in no way end up in the gen2 heap. I belive the problem is in the size of the gen0 heap size. But I'm not sure. I know that in Java there is a possibility to set a minimal size of the generational heaps. Is there such a way in .NET?

回答1:

The size of the different generations is an implementation detail, and I am not aware of any ways to tune it for .NET applications. If my memory serves me correctly generation 0 and 1 shares a single segment, which is 16 MB in Win32, so if you do create a whole lot of objects, some of these will be promoted to higher generations if they are still referenced (just as you describe).

I guess the idea behind limiting the size of generation 0 is to make sure a g0 collection is cheap. If generation 0 could grow to any size, your overall performance would most likely suffer.

EDIT: I believe Jeffrey Richter's book has some details on this, so you may want to check that out.

EDIT2: Richter states (p. 502-507) that generation 0's initial budget is 256 KB and generation 1's initial budget is 2 MB. However, that is not the size of the generation. The budgets are adjusted as needed and will grow and shrink according to the application's memory usage.

Joe Duffy's Professional .NET Framework 2.0 however, states that the ephermal generations (i.e. gen 0 and 1) shares a single segment, which is usally 16 MB (p. 117). Only generation 2 is allow to grow as needed (I assume LOH is allowed to grow as needed as well, but that is not clear to me from the text).



回答2:

Are you sure you are no longer referencing these objects? The GC is very good at tuning itself to the needs of your application and would not promote objects to Generation 2 unless you had roots for those objects somewhere.

I think if you figure out where these roots are and ensure that you really are no longer referencing these objects anywhere then the GC will start freeing the memory for those objects and never promote them to Generation 1 let alone Generation 2. Also if you do this then the GC will detect that you need Generation 0 to be larger and will increase the size of the heap on your behalf.



回答3:

Thank you guys for your help.

Whell, the sitution is very interesting. I've never mentioned that i'm storing those 30MB of objects in an array whoose size is 100000. I'm first allocating that array and then filling it with objects. Since that array is larger than 85K that array is stored in the Large Objects Heap. It turns out that for the garbage collection to collect objects in that heap, it needs to run gen2 collection, so every time there is not enough space in the Large Objects Heap, it runs the gen2 collector, which is trashing the performance. Here is the simple example that will constantly call gen2 collection. You need to run it in debug mode (because of compiler optimizations).

static void Main(string[] args)
{
     while (true)
     {
         byte[] objects = new byte[100000];
     }
}

Does this mean that whenever I need a temporary array whose size is larger than 85K, that array will end up in the larget objects heap?



回答4:

The Generation 0 heap is initially sized to your CPU's cache. This is so that small temporary objects don't even need to be moved to main memory.



回答5:

At first generation0 may increase and decrease but these are in future. when process runs the initialized managed heap has fixed size in generation0 256 KB (which meant Richter) because modern computers have 256 and more memory for 2-nd level CPU's cash. Second. Andrew, you are right. When some object is so large it goes to gen2 at once. I can suppose you wanted to decrease gen0 size to provoke more frequently called GC to clear unused space and keep your app lighter. I have same problem in a SilverLight web site and search a solution. I think what if code creates much object which have roots and when gen0 is full CLR calls GC which move them to gen1, and then when objects grow more move them to gen2. Let's say all 3 generation are nearly full. Then for example GC called. What it will do. Just clear generation0 ? How with 1-st and 2-nd ones. I think sollution to keep memory light is the following. First create small and much objects instead of large in size and less in count. Second objects internal referencing must be one way oriented and not chaotic referencing to each other. Static objects which are not created dynamically for example according to comming records from database, need to keep in global variables and not create again when code cals them.



回答6:

+1 to Andrew. The GC is self-tuning it learns about the app's memory need patterns on the fly.
In your case, if the GC performs a collection of Gen 0 and finds that a lot of objects survived / not a lot of memory was reclaimed, the garbage collector will grow the Gen 0 budget/quota automatically.

Looking at GC Type members, there doesn't seem to be a way to programatically configure GC Generation budgets/size.



回答7:

I don't think there's any option to set these values in .NET GC.

At first glance, looks like this particular situation justifies the need to have such advanced options, but if this would be a static option that it will affect all the lifetime of you program. A dynamic option, that you could set at runtime would be ideal.

Also, I don't think this will hugely affect the performance of your program, although if you where able to increase the gen(0) size you could free some memory (and gain some performance).

Because the GC is self-tunning, gen(0) will automatically increase size and the BIG objects will end up being garbage collected, sooner or later.