Home > Guides > Programming > Java

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

VisualGC

Last updated Apr 7, 2006.

You may have read about the Sun HotSpot JVM and how it handles garbage collection, but aside from verbose garbage collection logs, the JVM does not ship with any tools to visualize garbage collection behavior. With the release of HotSpot 1.4.2 and later, Sun provided a set of lightweight performance and configuration instrumentation and a set of monitoring APIs and tools for monitoring the performance of a HotSpot JVM in a production environment. Sun claims that it runs in an "always on" mode with negligible performance impact. You can read more about jmvstat as well as download it from the jvmstat 3.0 homepage:

http://java.sun.com/performance/jvmstat/

In addition to APIs, jvmstat provides a tool to visualize garbage collection behavior, called VisualGC. This article provides an overview of the Sun garbage collection strategy and then reviews the VisualGC tool.

Sun Garbage Collection Strategy

The Sun JVM is generational, meaning that it partitions its heap into generations. Specifically it defines the following two generations in the heap:

  • Young generation
  • Old generation

It further subdivides the young generation into three spaces:

  • Eden
  • Two survivor spaces, sometimes called the from space and the to space

All objects are created in Eden, as all life was created in the Garden of Eden, and when Eden becomes full then the JVM triggers a garbage collection. The garbage collector iterates over all items in Eden, freeing memory for each unreferenced object, and copies live objects into the first survivor space. If the survivor space fills up and there are still items in Eden then the items remaining in Eden are copied to the Old Generation. When Eden again fills up, its contents, plus the contents of the first survivor space, are copied to the second survivor space. This behavior is illustrated in figure 82.

Figure 82

Figure 82. Sun HotSpot JVM garbage collection example

At this point, objects are copied back and forth between the two survivor spaces and Eden until the Old Generation becomes full, triggering a mark-sweep-compact garbage collection. The former collection is referred to as a copy collection, or a minor collection, whereas a mark-sweep-compact collection is referred to as a major collection. A minor collection runs as a background process while the JVM is processing Java code. A major collection requires that all threads be frozen while it runs, meaning that all processing must wait until it completes.

A mark-sweep-compact collection performs the following steps:

  1. Construct a collection of objects referred to as the root-set. This set contains all objects that are directly visible by all threads
  2. Perform the reachability test by following each object reference from the root-set, marking each object that is reachable
  3. Iterate over all objects in the heap, sweeping away (freeing) all objects that are not marked
  4. Defragment the heap (compact) to reclaim space between freed objects

The reachability test is illustrated in the next figure.

Figure 83

Figure 83 Reachability test

Minor collections are inexpensive, and if they occur on a regular non-frequent interval (at least a few seconds between invocations) they can limit the impact of memory management on your application's performance. Major collections are expensive, and when they occur they significantly affect the performance of your application.

The reason for defining the JVM garbage collection this way is based upon the concept that most objects will be short-lived: they will be created for some purpose, used, and then discarded. When this occurs they can be reclaimed in Eden or in one of the survivor spaces without requiring a full mark and sweep garbage collection to reclaim their memory. The difficult part in fine-tuning garbage collection behavior is that different applications behave differently. Specifically Web-based, or request-based, applications tend to create a significant amount of short-lived objects very quickly. In this scenario, objects can be created in Eden, copied to survivor spaces, and then tenured (copied to the old generation) before the garbage collector has time to reclaim them. Then the old generation will become littered with dead short-lived objects, thus requiring a major garbage collection to reclaim their memory.

The default configuration for the Sun heap varies by operating system, but across the board is not sufficient to support an enterprise application. The young generation is sized much too small (32MB or 64MB depending on the operating system) relative to the heap size and the survivor spaces are tiny (the default size of the survivor spaces is 1/34th of the young generation.)

As a rule of thumb when configuring a request-based application heap, the young generation should be a little less than half the size of the heap and the survivor spaces should be between 1/6th and 1/10th the size of the young generation. These values can be configured by specifying command line arguments in table 1.

Table 1. Memory arguments for the Sun JVM

Argument

Description

-XmxNNNm

The maximum size of the heap in megabytes (m) or gigabytes (g)

-XmsNNNm

The minimum size of the heap in megabytes (m) or gigabytes (g)

-XX:NewSize=NNNm

The size of young generation

-XX:MaxNewSize=NNNm

The maximum size of the young generation

-XX:SurvivorRatio=N

The ratio of Eden size to survivor space size

For example, to configure a one gigabyte heap, I typically use the following parameters:

java –Xmx1024m –Xms1024m –XX:NewSize=448m –XX:MaxNewSize=448m –XX:SurvivorRatio=6 com...

This command line creates a 1GB heap (1024m) that starts at 1GB and does not grow because the minimum and maximum are the same. It has a 448MB young generation (which is a little less than half) that is fixed in size because the minimum and maximum sizes are the same. And it defines a survivor space that is 1/8th the size of the young generation, or about 56MB. The survivor ratio is confusing because a value of 6 means that each survivor space receives 1/8th of the young generation. The best way that I like to think about it is that when you specify a value of 6, you are saying that Eden receives six units and each survivor space receives one unit. Likewise if we specified a ratio of 8, then Eden would receive 8/10ths of the heap and each survivor space would receive 1/10th of the heap. This configuration has served my customers very well in my consulting activities. And for reference, here is a good starting configuration for a 2GB heap:

java –Xmx2048m –Xms2048m –XX:NewSize=768m –XX:MaxNewSize=768M –XX:SurvivorRatio=6

During tuning exercises I will start with these values and then observe the behavior of garbage collection to determine whether or not they are effective. And my criteria for effectiveness of garbage collection are:

  • Very infrequent major collections, such as every half hour or less in a user loaded environment
  • Regular minor collections, such as every 10-20 seconds in a user loaded environment, but that complete in less than a second; note that a larger young generation increases the length of time that is spent in minor collections, but typically in the order of tenths of a second but recall that these collection do not freeze running threads

The last generation in the JVM process memory that is not actually part of the heap, but is configurable and can dramatically affect the performance of your application is the permanent generation. The permanent generation, or permanent space, is an area in the JVM process memory that holds class files. Consider that in order to create an instance of a class in the heap, the JVM needs to open the ".class" bytecode file to determine what to create on the heap. It loads this .class file into the permanent generation and caches it there to create future objects. By default, classes can be unloaded from the permanent generation when it runs low on memory, but that option can be overridden with the –noclassgc command line argument. My recommendation is to never use this option because although there is a performance boost in using it (if a class stays in memory then it is only loaded once), if the permanent generation runs out of memory and it is not able to unload classes then the JVM will crash. I've seen this happen at very large customer sites, and since then my opinion is that I would rather take a periodic slow response time instead of a JVM crash.

You can configure the size of the permanent generation to minimize class unloading by configuring the following two command line arguments:

Table 2. Permanent generation memory arguments for the Sun JVM

Argument

Description

-XX:PermSize=NNNm

The initial size of the permanent generation in megabytes (m) or gigabytes (g)

-XX:MaxPermSize=NNNm

The maximum size of the permanent generation in megabytes (m) or gigabytes (g)

For example:

-XX:PermSize=128m –XX:MaxPermSize=128m

As a rule of thumb, 128MB is typically pretty good, but if you have a large number of classes and JSPs then you might consider increasing it to 256MB. I have configured one 512MB permanent generation, but that is excessively large and I only did so to work around some core architecture issues in the application.

VisualGC Tool

The jvmstat API provides insight into all that we have been discussing in this article, but if you do not want to write your own monitoring tool, jvmstat includes a tool called VisualGC. Again, you can download this tool here:

http://java.sun.com/performance/jvmstat/

And product documentation is included here:

http://java.sun.com/performance/jvmstat/visualgc.html

After you download and install jvmstat, start a JVM by launching your application server (e.g. Tomcat, JBoss, WebLogic, and so on). Next execute the jps command that ships with your Java 5 JRE (it is in the bin directory.) This displays the process ids for all running JVMs on your computer.

Next launch visualgc.cmd from the jvmstat bat directory on Windows or launch visualgc from the jvmstat bin directory on unix, passing it the process id of the JVM that you want to monitor. This will display three windows, such as those shown in figure 84.

Figure 84

Figure 84 VisualGC screenshot

The left window displays a break down of the permanent space utilization, old generation utilization, and the young generation, as Eden and the two survivor spaces (S0 and S1.) The right window shows historical behaviors of each space as well as the time spent compiling bytecode to native code (compile time), loading and unloading classes (class loader time), and garbage collection time (GC time.) The bottom window displays a histogram snapshot of the age distribution of objects in the active survivor space after the last young generation collection.

In general you want to observe how quickly Eden becomes full and how much of its data is successfully copied to a survivor space and how much is copied to the old generation. If Eden fills too quickly then you need to increase the size of the young generation (and hence Eden.) If too many objects are copied directly to the old generation or the old generation is growing too quickly, you need larger survivor spaces.

Summary

JVM heap tuning is a non-trivial task, but with the metrics that jvmstat provides and the visualization of those metrics that the Visual GC tool provides, it becomes much more methodical. Start with the best practices configuration that I described in this section and then monitor your heap using Visual GC to determine if you need to increase or decrease the sizes of the young generation or its survivor spaces.

Discussions

Read and display the table in the document
Posted Nov 12, 2008 06:01 AM by StrongHead
1 Replies
Correction
Posted Nov 4, 2008 06:09 PM by youssef.mohammed
1 Replies
Instead of synchronising getInstance
Posted Nov 3, 2008 05:42 AM by grahamkelly
1 Replies

Make a New Comment

You must log in in order to post a comment.

Related Resources

Dustin SullivanIf You Are New to Java Programming...
By Dustin SullivanJune 2, 2009 No Comments

We recently sat down with several top Java developers to talk about that state of the language as we approach this year's JavaOne.  As we were wrapping up, we threw one last question at them out of curiosity, and we thought you'd like to see what some of them said.

Steven HainesOracle Buys Sun of $7.4B
By Steven HainesApril 20, 2009 No Comments

In a stunning turn of events, Oracle steps in and buys Sun amist the breakdown of IBM's attempt to acquire Sun.

Steven HainesIBM in talks to buy Sun Microsystems for at least $6.5B
By Steven HainesMarch 18, 2009 No Comments

Reuters reported this morning that IBM is in talks to buy Sun Microsystems, which could "bolster their computer server products against rivals such as Hewlett-Packard Co."

See More Blogs

Informit Network