• Java |
    Monday, September 11, 2006

The Second Most Important GC Tuning Knob

is NewRatio or it's equivalent. Number 1 would be the total heap size. And just so the guys down the hall don't start throwing rocks at me, the 0-th most important GC tuning knob is the choice of the type of garbage collector itself. But let's talk about number 2.

NewRatio is a flag that specifies the amount of the total heap that will be partitioned into the young generation. It's the tenured-generation-size / young-generation-size. A NewRatio value of 2 means that the tenured generation size is 2/3 of the total heap and the young generation size is 1/3. There are other ways of specifying that split. MaxNewSize will specify the maximum size of the young generation. The flag -XmnNNN is equivalent to -XX:NewSize=NNN and -XX:MaxNewSize=NNN. NewSize is the initial
size of the young generation.

Say you've chosen the right size for your overall heap. But does the value of NewRatio split the space between the young generation and the tenured generation optimally. If your application has a large proportion of long lived data, the value of NewRatio may not put enough of the space into the tenured generation. I recently was looking at the performance of an application where too many major collections where being done.
The problem was that the long lived data overflowed the tenured generation. When a collection was needed
the tenured generation was basically full of live data. Much of the young generation was also filled with long lived data. The result was that a minor collection could not be done successfully (there wasn't enough room in the tenured generation for the anticipated promotions out of the young generation) so a major collection was done. The major collection worked fine, but the results again was that the tenured generation was full of long lived data and there was long lived data in the young generation. There was also free space in the young generation for more allocations, but the next collection was again destined to be a major collection. By decreasing the space in the young generation and putting that space into the tenured generation (a
value of NewRatio larger than the default value
was chosen), there was enough room in the tenured generation to hold
all the long lived data and also space to support minor collections. This particular
application used lots of short lived objects so after the fix mostly minor collections
were done.

Why not just make NewRatio conservatively large to avoid this situation? You want your young generation to be large enough so that objects have a chance to die between collections. Applications with many threads may be allocating at a high rate so a large young generation might be appropriate.

The point here is that if you choose your overall heap size carefully, also think about the size of the young generation. This is particularly true if the maximum size of your heap is
constrained for some reason (e.g., by a limited amount of physical memory).

Something to add here is that the default value of NewRatio is platform dependent and runtime compiler (JIT) dependent.
Below are the values as we get ready for the JDK6 release.

-server on amd64     2
-server on ia32 8
-server on sparc 2
-client on ia32 12
-client on sparc 8

These reflect the expectation that the server compiler will be used on applications with more threads and so have higher allocation rates.

And, finally, if you're about to ask why there are different ways to set the size of the young generations:

NewRatio gives you a way to scale the young generation size with the total heap size.

NewSize and MaxNewSize give you precise control.

-Xmn is a convenience

Join the discussion

Comments ( 5 )
  • Damon Hart-Davis Tuesday, September 12, 2006

    Interesting: I would not have considered this 2nd.

    How does this interact with CMS (and its instant tenuring of anything that survives one young GC)?



  • Jon Umasamits Tuesday, September 12, 2006
    Yes, ranking NewRatio as second is something of a judgment call. I haven't run the experiment but I would expect that the effect would be similar with CMS - a young generation collection would be attempted and the amount of free space in the CMS generation would not be sufficient for anticipated promotions resulting in a collection of the CMS generation. As with the other collectors, a role of the young generation is to filter out short lived objects from being promoted into the CMS generation. The filtering is done more efficiently by a young generation collection than by other types of collections. If a young generation collection cannot be supported, a CMS collection has to be done. If you're lucky, CMS would anticipate that the next young generation collection would not succeed and start a concurrent collection early. If you're not so lucky (i.e., a young generation collection was actually started and failed) then a full stop-the-world collection would be done.

    What would you nominate as the second most important GC flag?

  • Damon Hart-Davis Tuesday, September 12, 2006

    If it existed/worked, I guess it would have to be a cross-GC ergonomics adjustment such as pause-time (or throughput or GC parallelism) target.

    I think that NewRatio is a 2nd-order parameter that the GC subsystem is in a much better position to monitor and tune dynamically than I can statically, cf static C++ optimisation/compilation vs HotSpot. These things don't frighten me one bit, but I feel that I have little good intuition on how to adjust it.



  • Mikael Gueck Saturday, September 23, 2006
    When Java sources get opened, will you also open some GC test suites which we can use to simulate our typical loads and find the optimal GC parameters with?
  • Jon Umasamist Tuesday, September 26, 2006
    I don't have a good answer for whether GC test suites will be given away. I think the vast majority of the tests we've written pound on a particular aspect of our implementation. The tests try to break GC, not measure its performance in any sane way. We have some simple tests that have very uniform allocation patterns (e.g. lots of objects of a few sizes) with a few knobs to change behavior. These are meant to be simple so that we can study how our GC responds. Then we have the standard benchmarks which we usually run en masse to look for surprises (hey, why did GC go crazy on XYZ?). After that there are mostly just real applications. I don't suppose that's what your asking for. I'll ask around but don't really think anyone knows yet.
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha