The Fault with Defaults

During early work on the low pause collector some of the applications we used for benchmarking the collector had the characteristic that objects had very short lifetimes or relatively long lifetimes with not much in between. All our current collectors use a copying collection for the young generation. This copying collection will copy the younger objects back into the young generation and copy the older objects into the tenured generation. The expectation is that more of the younger objects will die in the young generation if they are give a little more time. A consequence of this is that long lived objects are copied repeatedly into the young generation before finally being promoted into the tenured generation. Depending on the mix of lifetimes of your objects, this can be good (more objects die in the young generation) or this can be bad (extra copying into the young generation). Additionally at about the same time we were discussing the policy of never doing the copying but rather always copying any objects that survived a young generation collection immediately into the tenured generation. There are some collectors that do that and there was some anecdotal evidence that it was a good strategy. Given some example applications where never copying back into the young generations seemed to be the right strategy and the general discussion about what was the right policy for copying, we decided to make the default behavior for the low pause collector to always promote objects surviving a young generation collection into the tenured generation.

This default policy still seems reasonable for some applications but the old adage about one size not fitting all certainly applies here. So here's what you should consider and what you can do.

The young generation is acting like a filter to dispose of the shorter lived objects. Any objects that get through the filter are moved to the tenured generation and are henceforth handled by the mostly concurrent collections (which I'll just call the concurrent collections from here on out). If the concurrent collections are starting too often to suit your tastes, one possibility is to filter out more of the short lived objects by doing some copying during the young generation collections. This may slow down the growth in the number of objects in the tenured generation and lessen the need for too frequent concurrent collections. To do this try the command line flags

-XX:MaxTenuringThreshold=15 -XX:SurvivorRatio=8

MaxTenuringThreshold is the number of collections that an object must survive before being promoted into the tenured generation. When objects are copied back into the young generation during a young generation collection, the objects are copied into an part of the young generation that is referred to as a survivor space. SurvivorRatio set to 8 will mean that the survivor spaces are about 10% of the size of the young generation. The survivor spaces and SurvivorRatio are described in the 5.0 tuning guide that can be found under

The FAQ attached to the 5.0 tuning guide has an entry about experimenting with MaxTenuringThreshold.

By the way having concurrent collections starting "too often" can also be a sign that the tenured generation is smaller than is needed for good performance. You can always try increasing the size of the heap to reduce the frequency of the concurrent collections. The suggestions above may be able to get you the less frequent concurrent collections without the longer concurrent collections that typically come when the heap is increased.

The default setting to always promote objects that survive a young generation collection is only the default for the low-pause collector. The other collectors by default will do some copying before promoting an object to the tenured generation.


Most of the apps I worked on were best served by a relatively large new generation and tenuring thresholds of 32, but still it is annoying to have all long lived object being copied N times unnecessarily, particularly at startup. I tried to trick this behavior with the Initial tenuring threshold + a max, but really it would be really usefult to be able to mark object as long lived so the GC can move them efficiently to the old generation, and keep the good behavior of copying GC in the new gen.

Posted by Brice Beard on April 04, 2006 at 02:35 AM PDT #

It would certainly make our lives easier if we got hints about the expected life times of objects, but I'm guessing that it would be difficult for most users to provide that information. There is the concept of pre-tenuring objects (i.e., allocating certain objects directly into the tenured generation) but the usefulness of the idea in hotspot is very, very limited. You can say that you want large objects beyond a certain size to be considered for pre-tenuring. I can't recall anyone using it. Another group at Sun took a look at the idea of identifying allocation sites at which long lived objects were created. Later allocations from those sites could then be pre-tenured. Maintaining the information requires both time and space, and I think the idea needed more tuning before the benefits out weighed the costs.

The throughput collector with GC ergonomics tries a different approach. The collector dynamically adjusts the tenuring threshold in order to move GC work to or from the young generation collections. If the GC ergonomics decides that more work is being done in the young generation collections than in the tenured generation collections, the tenuring threshold is lowered to more quickly move objects into the tenured generation. This a coarse adjustment when compared to pre-tenuring (which is at the object level) and certainly does not take the place of pre-tenuring. It does try to adjust the copying costs to reduce the overall cost of GC.

Posted by Jon Masaumits on April 04, 2006 at 04:26 AM PDT #

Post a Comment:
Comments are closed for this entry.



« June 2016