Home > Articles > Programming > Java

Java Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

Adaptive Garbage Collection

Last updated Mar 14, 2003.

Prior to version 1.5 of the JVM, much of a programmer's performance tuning time was spent in properly sizing the various "compartments" of the heap. If this is a new subject to you, consider the following a very brief primer on heap tuning:

The configuration of the heap is different for different vendor implementations. Thus, tuning the Sun JVM, IBM JVM, and BEA JRockit JVM are all different. For the purposes of this discussion, I will docus on the Sun JVM. The Sun JVM heap, hitherto referred to simply as the "heap," is divided into two regions, referred to as generations:

  • Young Generation

  • Old Generation

The young generation is further divided into three subregions:

  • Eden

  • Survivor Space 1

  • Survivor Space 2

Objects are created in Eden (yes, as in the Garden of Eden), and hopefully they are destroyed in Eden. When Eden becomes full, the object is moved to the first survivor space; when that becomes full it is moved to the second survivor space; finally, objects that are not destroyed in the second survivor space are retired to the old generation.

The premise is that most objects should be created and destroyed in a relatively short interval, which is the case for most client applications and some server applications.

Garbage collection runs periodically (as needed) with the purpose of locating objects that are no longer referenced. When it finds one, it marks the memory that the object had used as available. The process of running through any of the three regions in the young generation searching for "garbage" is called a minor collection and is relatively painless for the JVM; it can search the regions independently, and each is relatively small. The process of running through the old generation however, is a painful experience that not only takes a long time to run, but brings everything running in the heap to a complete stop. An improperly tuned heap running in a J2EE environment can bring the entire server to a stop for more than a minute every couple minutes; how would your boss react to a Web server that took 90 seconds to respond on a regular basis?!

Garbage collection is one of Java's biggest benefits, but if not used cautiously, it can be one of its biggest limitations. The two characteristics that affect garbage collection performance are:

  • Pause: the amount of time that the JVM pauses while performing garbage collection

  • Throughput: the ratio of garbage collection time to application time

The goal is to minimize the pause and maximize the throughput. Yet, there is a tradeoff in defining the sizes of each region: the larger the region, the less frequently it needs to be collected, but the longer each collection will take. In previous versions of the JVM, we used to specify the respective sizes of the young generation's three regions and old generation, load test the application with representative transactions, and observe the behavior of the heap. The desired pattern is a slow build-up of memory usage in the heap to a critical mass, and then a stabilization where sequential minor collections can be observed.

As a general starting point for tuning an enterprise application I recommend the following:

  • Size the young generation a little less than half the size of the heap. It must be less than half the size of the entire heap by definition, but not much smaller!

  • Size Eden to be somewhere between one half and two thirds the size of the young generation

  • Size each survivor space to be between one quarter and one eighth the size of the young generation

The process continues by "testing and tweaking" until things look good.

The 1.5.0 JVM garbage collection has been updated in several ways to address the problems with garbage collection directly. It defines an adaptive sizing policy that will dynamically resize the heap and its respective regions with the following goals:

  • Adhere to a desired maximum garbage collection pause

  • Adhere to a desired application throughput goal

  • Minimize the footprint of the heap

To accomplish this you must enable the adaptive sizing policy by passing the JVM the following argument:

-XX:+UseAdaptiveSizePolicy

To define the desired policy values, use the following flags:

-XX:MaxGCPauseMillis=nnn

A hint to the virtual machine that pause times of nnn milliseconds or less are desired. The vm will adjust the java heap size and other gc-related parameters in an attempt to keep gc-induced pauses shorter than nnn milliseconds. Note that this may cause the vm to reduce overall throughput, and in some cases the vm will not be able to meet the desired pause time goal.

-XX:GCTimeRatio=nnn

The ratio of GC time to application time. 1 / (1 + nnn) For example: XX:GCTimeRatio=19 sets a goal of 5% of the total time for GC.

So, rather than spending your time trying to manually adjust your heap parameters to meet performance expectations, you can instead ask the heap to adjust itself at runtime to meet those same performance expectations on your behalf.

The feature is in place, but we must still wait to see if it delivers on its promise. Currently, no application servers support version 1.5 of the J2SE. Once we have a valid test environment, we can setup load tests and see how the JVM holds up.

Summary

In this article we have discussed some of the new ease of use and presentation features of the new Java 2 Standard Edition, version 1.5:

  • Generic Types

  • Enhanced For Loops

  • Autoboxing

  • Enumerated Types

  • Formatted Input and Output

  • Variable Arguments

  • New Look-and-feels

Next we looked at how garbage collection has been greatly improved in Tiger and look forward to a good testing environment where we can validate their claims.

In the next installment we will look deeper under-the-hood of Tiger and see the new management interfaces as well as the new facilities for meta-data.