Print Story http://java.sys-con.
com/node/84695/print
Close Window
Print Story
Know Your Worst Friend, the Garbage
Collector
All Java programmers are aware of a peculiar entity living in their Java Virtual Machine known as the
Garbage Collector. Although we all use it every day, only a few of use know exactly what it is and how
it works. Unquestionably useful, the Garbage Collector can hurt the performance of your application
without you even knowing it. In this article you will learn about its inner workings and understand
how to tame it to boost your programs.
The Garbage Collector, which I will call GC from now on, is a
benevolent sentinel present in every JVM. Its role is to identify
and free chunks of memory left unused by the currently running
application. Its job should shed some light on the origin of the
name "garbage collector." Despite the depreciating name, the
GC is like Dilbert's garbage man who is very smart, even
smarter than you sometimes.
During its lifecycle, an application creates a certain number of objects, that is to say a certain amount
of data that consumes memory and lasts according to its role in the inner workings of the application.
Let's take the simple example of a Web browser. The object corresponding to the window you look at
has exactly the same life span as the application itself, while the object standing for the Google logo
on the google.com front page lasts only as long as you're on the page. As a matter of fact, an
application spawns a large amount of short-lived objects during its life span. So you can understand
that defining and controlling the lifecycle of every single object generated by an application demands a
tremendous amount of work from developers.
A simple example should give you a better idea of the incredible number of objects that are born and
then killed. When you open a plain text file in a text editor, in our case Jext, 342,997 objects are
created and destroyed. Even the simple conversion of a pound to kilograms entails the birth and the
death of more than 171,000 objects in Numerical Chameleon. Imagine if the programmer had to know
exactly how to handle every single object during an application's lifecycle. Yet, the slightest error can
prove disastrous; this is how infamous memory leaks happen. A memory leak is caused by objects,
called undeads or zombies, left unused but still marked alive in memory. The more undeads wandering
about, the more memory the application will need. The program eventually runs of out fresh memory
and crashes. Good memory management is one of the most difficult issues in low-level languages like
C or C++. Some high-level languages, like Java or Python, rely on a GC that cuts down the
programmer's workload. So a developer supported by a GC only needs to do object creation.
Although extremely convenient, a GC is not a miracle tool and every so often does wicked things.
Sheltered behind this powerful shield against memory management, the developer can easily provoke
a disaster and wind up blaming the GC, the language, or the platform for his own mistakes. By
studying and understanding the inner workings of the GC you can optimize the Java Virtual Machine
according to the specific needs of your applications. Best of all, you won't have to change the source
code. And you'll be able to use these trick with applications you didn't write.
Garbage collectors have been around for a long time and dozens of algorithms have been devised to
1 of 5 05/06/2011 13:55
Print Story http://java.sys-con.com/node/84695/print
implement different collection strategies. The behaviors and properties we're about to discuss are
specific to Sun's HotSpot JVM 1.4.1 and higher. Developers are free to choose their own memory
reclaiming strategy when writing a GC, which is why we must focus on a particular implementation to
learn how to optimize our programs accurately. You should also know that the same JVM version
yields different results when executed on different platforms. For instance, the performance of Java
software running on Solaris is boosted by tweaking the threads management.
The GC in the HotSpot JVM is also called the "generational garbage collector." As its name suggests,
this GC can make a difference to several generations of objects. Within the virtual machine, objects
are born, live, and die in a memory area known as the heap. The heap itself is divided into two parts,
each one corresponding to a given generation: the young space and the tenured space. The first hosts
recent objects, also called children, while the second one holds objects with a long life span, also
called ancestors. Next to the heap, the virtual machine contains another particular memory area, called
the perm, in which the binary code of each class loaded by the currently executing program is
archived. Although the perm is important to applications dynamically generating a lot of bytecode,
like a J2EE server, it's unlikely you'll ever need to tweak it. Beware though; some unexpected
applications might need a lot of perm space. For instance, IBM's XSLT library Xalan consumes perm
space when style sheets are memory-compiled. If you ever encounter a vicious OutOfMemoryError,
think of the perm space. Figure 1 depicts the heap and its structure.
Both the tenured space and the young space contain a virtual space, a zone of memory available to the
JVM but free of any data. That means that those spaces might grow and shrink with time. The way
these spaces change their size is an important HotSpot setting. You can see three other memory areas
in the young space in Figure 1: the eden and two survivor spaces. To understand their purpose we need
to study the various algorithms used by the GC.
Whenever a new object is allocated to the heap, the JVM puts it in the eden. Survivors are treated in
turn. The GC uses the one that remains free as a temporary storage bucket. When the young space gets
overcrowded, a minor collection is done. A very simple copy algorithm is used that involves the free
survivor space. During a minor collection, the GC runs through every object in both the eden and the
occupied survivor space to determine which ones are still alive, in other words which still have
external references to themselves. Each one of them will then be copied into the empty survivor space.
Quickness is the main advantage of this algorithm since it doesn't need to reclaim memory, strictly
speaking. This copy algorithm is fostered by a feature in the modern JVM that ensures that the heap is
a single contiguous memory segment. This is why it can't exceed about 1.5GB on Windows.
Microsoft's OS splits the memory address space of a 32-bit process in half by allocating 2GB to the
kernel and 2GB to the application. Theoretically the JVM should be able to allocate up to 2GB of
contiguous memory but it can't because of the memory overhead used by the OS and the JVM itself.
The perm is an example of this overhead. Anyway, at the end of a minor collection, both the eden and
the explored survivor space are considered empty. As minor collections are performed, living objects
proceed from one survivor space to the other. As an object reaches a given age, dynamically defined at
runtime by HotSpot, or as the survivor space gets too small, a copy is made to the tenured space. Yet,
most objects are born and die right in the young space. To obtain ideal performance, the JVM should
be tweaked to avoid as many copies from the young space to the tenured space as possible.
In the tenured space the laws are different. Whenever more memory is needed, a major collection is
done with the help of the Mark-Sweep-Compact algorithm. Though it's not complex, it's greedier than
the copy algorithm. The GC will run through all the objects in the heap, mark the candidates for
memory reclaiming, and run through the heap again to compact remaining objects and avoid memory
fragmentation. At the end of this cycle, all living objects exist side-by-side in the tenured space. The
major collections responsible for most Java applications slow down from time to time. Unlike a minor
collection, running a major collection stops the execution of the whole application. So a good
2 of 5 05/06/2011 13:55
Print Story http://java.sys-con.com/node/84695/print
optimization trick is to reduce the burden of the Mark-Sweep-Compact algorithm.
Given this information, you should now be able to use the following JVM command-line options
sensibly. Each <size> tag can be replaced by a size measured in bytes (i.e., 32,765), kilobytes (i.e.,
96k), megabytes (i.e., 32MB), or gigabytes (i.e., 2GB).
-Xms<size> specifies the minimal size of the heap. This option is used to avoid frequent resizing
of the heap when your application needs a lot of memory.
-Xmx<size> specifies the maximum size of the heap. This option is used mainly by server-side
applications that sometimes need several gigs of memory. So the heap is allowed to grow and
shrink between the two values defines by the –Xms and –Xmx flags.
-XX:NewRatio=<a number> specifies the size ratio between the tenured space and the young
space. For instance, -XX:NewRatio=2 would yield a 64MB tenured space and a 32MB young
space, together a 96MB heap.
-XX:SurvivorRatio=<a number> specifies the size ratio between the eden and one survivor
space. With a ratio of 2 and a young space of 64MB, the eden will need 32MB of memory
whereas each survivor space will use 16MB.
Choosing a value for each parameter is a difficult job that depends solely on the application you're
attempting to tweak. An application creating large amounts of short-lived objects, like an HTTP server
for instance, won't have the same needs as a largely static application, like a screen saver. Sun provides
a helpful document about many HotSpot options called Java HotSpot VM Options. You should also
read A Collection of JVM Options by Joseph Mocker who gathered a comprehensive list of Sun's
JVM command-line flags.
Besides the heap, HotSpot can change the behavior of the GC itself. Although the algorithms don't
differ much, you can choose among three different execution modes. The GC interrupts the application
for minor and major collections. This is a troublesome behavior on multiprocessors where you lose a
lot of CPU power every time the GC is used. You can lessen, if not avoid, the impact on performance
by carefully picking the GC execution mode.
The first one is called incremental garbage collection. As its name suggests, this mode does a bit of
major collecting every time a minor collection is run. Global performance suffers from this execution
mode but you get rid of those nasty pauses caused by major collections. Incremental garbage
collection also tends to leave the heap in a fragmented state. So it's a good idea to limit its use to
applications with long-lived objects. It can help you in a subtle way to obtain more responsive Swing
UIs.
The second mode is a parallel GC using many threads at once, at least one per processor, to do minor
collections. If your target computer has less than three processors, it is unlikely you will ever notice a
difference. This option is particularly well suited to heavy servers running applications spawning
many short-lived objects like web servers.
Finally yet importantly, a concurrent GC can do incremental major collection without interrupting the
application for long periods of time. A concurrent GC can also use parallel execution for minor
collections. According to Sun Microsystems, this execution mode gets good results with interactive
applications running on a single-processor computer. To choose among these three modes and set them
up, you need to use the following command-line flags:
-Xincgc activates incremental garbage collection.
-XX:+UseParallelGC activates the parallel GC. The number of threads used to do minor
collections is defined by the -XX:ParallelGCThreads=<a number> flag.
-XX:+UseConcMarkSweepGC activates the concurrent GC. Parallel minor collections can be
3 of 5 05/06/2011 13:55
Print Story http://java.sys-con.com/node/84695/print
activated as well by using the -XX:+UseParNewGC flag.
To measure the impact of your choice on your application, you'll want to use the –verbose:gc
command-line flag. You'll then get a GC activity trace on the standard output. Redirecting the standard
output to a file and running the following Python script will let you draw very useful charts as shown
in Figure 2.
This script can be used by specifying a file name as the first parameter or by piping it directly to the
Java command.
#!/usr/bin/env python
# -*- coding: ISO-8859-1 -*-
import fileinput
import re
print "%s\t%s\t%s\t%s" % ("Minor", "Major", "Alive", "Freed")
for line in fileinput.input():
match = re.match("\[(Full )?GC (\d+)K->(\d+)K\((\d+)K\), ([^ ]+) secs]", line)
if match is not None:
minor = match.group(1) == "Full " and "0.0" or match.group(5)
major = match.group(1) == "Full " and match.group(5) or "0.0"
alive = match.group(3)
freed = int(match.group(2)) - int(alive)
print "%s\t%s\t%s\t%s" % (minor, major, alive, freed)
The result is a CSV document using tabs as separators and containing four columns of data. The first
one gives the time consumed by the GC to do a minor collection, the second one gives the same
information for a major collection, the third one indicates the amount of memory used by the
application after the execution of the GC and the last one gives the amount of memory reclaimed by
the last collection. (This script was inspired by an AWK script written by Ken Gottry in the article
Pick up performance with generational garbage collection published on javaworld.com.)
The chart presents the running time of each collection performed by the GC during the application life
cycle. This chart was generated with the following command line and OpenOffice.org:
$ java -verbose:gc -Xms64m -Xmx128m -XX:NewRatio=2 -XX:+UseConcMarkSweepGC -jar ../lib/jext-5.0.jar > gclog
$ gclog2csv.py gclog > gclog.csv
Although simple, this small script can help you tune your application pretty quickly. As of J2SE 5.0,
Sun Microsystems provides a more powerful tool called JConsole that will graphically monitor the
state of the heap and the activity of the GC as shown in Figure 3.
Further Reading
Java HotSpot VM Options, Sun Microsystems, http://java.sun.com/docs/hotspot/VMOptions.html
A Collection of JVM Options, Joseph Mocker, http://blogs.sun.com/roller/resources/watt/jvm-
options-list.html
Pick up performance with generational garbage collection, Ken Gottry,
www.javaworld.com/javaworld/jw-01-2002/jw-0111-hotspotgc-p4.html
Garbage Collection in the Java HotSpot Virtual Machine, Tony Printezis, www.devx.com/Java
/Article/21977?trk=DXRSS_JAVA
Garbage Collection Performance, Brian Goetz, www-106.ibm.com/developerworks/java/library
4 of 5 05/06/2011 13:55
Print Story http://java.sys-con.com/node/84695/print
/j-jtp01274.html
Using JConsole to Monitor Applications, Mandy Chung, http://java.sun.com/developer
/technicalArticles/J2SE/jconsole.html
© 2008 SYS-CON Media Inc.
5 of 5 05/06/2011 13:55