|
Articles Index
Virtual Machine Performance
By Ed Ort
February 2001
View or Download Article
Prior to Version 1.3, production releases of the
Java 2 SDK, Standard Edition
for the Solaris
Operating Environment included a virtual machine1
implementation known as Exact VM (EVM). With the release of
Java 2 SDK, Standard Edition 1.3, EVM is
replaced by the
Java HotSpot Server VM
and the
Java HotSpot Client VM.
The Java HotSpot Client and Server VMs are designed to deliver a high level of
performance for Java technology-based applications, in many cases a higher level
of performance than these applications could achieve with EVM.
However, as is true for most performance-enhancing tools, the performance
of an application can also depend on other factors such as VM options specified
at runtime.
This article presents the results of a test that compared the performance
of a Java application running with the JDK 1.2.2_05a Production Release
for Solaris, to the performance of that application running with the Java 2 SDK 1.3
Early Access release for Solaris. The test also measured the impact
of various VM option settings on performance.
These results can help you determine which version of Java 2 SDK for Solaris is best
for your needs, and what JVM settings are best for certain types of applications.
HotSpot Client and Server VM Distribution
The Java HotSpot Client and Server VMs are available as follows:
- The Solaris and Linux versions of Java 2 SDK, Standard Edition V 1.3
include the Java HotSpot Server VM and the Java HotSpot Client VM.
- The Windows NT version of Java 2 SDK, Standard Edition V 1.3 includes
the Java HotSpot Client VM.
- The Java HotSpot Server VM is available as a separate download for Windows NT.
Hardware Configuration
The performance test was run on the following hardware:
| Processor: |
Sun Ultra UPA/PCI (4 X UltraSPARC-II 450 Mhz) |
| System clock frequency: |
113 MHz |
| Memory size: |
4096 Mbytes |
Test Program
The performance test program is a multithreaded program
named HeapTest.
HeapTest uses classes HeapThread,
Barrier, and Node.
The HeapTest program iterates through a number of loops of work.
In each loop, it performs a set of memory allocation and computation tasks.
In the first loop, HeapTest performs memory allocation exclusively.
In the next loop, it performs primarily memory allocation and some computation.
With each succeeding loop, it lowers the amount of memory allocation
activity and raises the amount of computation activity, until the
final loop, in which all the work is computation.
No matter what mix of tasks it performs, the program equally distributes the
work it does across a number of threads.
The maximum number of threads HeapTest starts, and the number of loops
HeapTest executes, depends on arguments specified at runtime.
For example:
java heaptest.HeapTest 5 2
runs HeapTest (in the heaptest package) with a maximum of 5 threads,
and performs a maximum of 2 loops for either memory allocation or computation.
The maximum loop value of 2 results in HeapTest executing a set of tasks
three times: the first time, it performs two loops of memory allocation;
the second time, it performs one loop of memory allocation and one loop of computation;
the third time, it performs two loops of computation.
For each run, the program prints the total time to perform the work
(that is, memory allocation and computation)
for a given number of threads, up to the maximum number of threads available.
Here's an example of HeapTest output
for a maximum of 5 threads, and a maximum of two loops:
Threads 2 Heap, 0 CPU 1 Heap, 1 CPU 0 Heap, 2 CPU
1 8887 14203 18855
2 9836 14865 18657
3 10588 16572 18738
4 10253 16190 18303
5 10194 16675 18895
|
Test Configurations
The performance test comprised multiple runs of the HeapTest program
with either the Java 2 SDK 1.2.2_05a production release for the Solaris Operating
Environment, or the Java 2 SDK 1.3.0 EA Early Access release for the Solaris
Operating Environment.
(In this article, these individual runs are referred to as "tests.")
In each of these tests, HeapTest
executed with two separate combinations of Java 2 SDK and runtime options.
Here are the combinations:
- Java 2 SDK 1.2.2_05a with two different sets of runtime options.
- Java 2 SDK 1.3.0 EA with two different sets of runtime options.
- Java 2 SDK 1.2.2_05a with one set of runtime options,
and Java 2 SDK 1.3.0 EA with another set of runtime options.
If you think of one combination of Java 2 SDK and runtime options
for a test as "configuration A", and the other combination
of Java 2 SDK and runtime options for a test as "configuration B",
then each test captured four types of performance results, as follows:
- The time performing memory-allocation tasks with configuration A.
- The time performing memory-allocation tasks with configuration B.
- The time performing computation tasks with configuration A.
- The time performing computation tasks with configuration B.
The specific combinations of Java 2 SDK production release
and runtime options in effect for each test are as follows:
| Test Number |
Java 2 SDK Production Release |
Runtime Options¹ ² |
| Test 1 |
Java 2 SDK 1.2.2_05a
Java 2 SDK 1.2.2_05a |
default
-Xms256m -Xmx512m -Xgenconfig:64m,64m,semispaces:64m,512m,markcompact -Xoptimize |
| Test 2 |
Java 2 SDK 1.2.2_05a
Java 2 SDK 1.2.2_05a |
default
-Xms256m -Xmx512m |
| Test 3 |
Java 2 SDK 1.2.2_05a
Java 2 SDK 1.3.0 EA |
default
-server |
| Test 4³ |
Java 2 SDK 1.2.2_05a
Java 2 SDK 1.3.0 EA |
default
-server |
| Test 5 |
Java 2 SDK 1.3.0 EA
Java 2 SDK 1.3.0 EA |
-client
-server |
| Test 6 |
Java 2 SDK 1.3.0 EA
Java 2 SDK 1.3.0 EA |
-server
-server -XX:+UseBoundThreads |
| Test 7³ |
Java 2 SDK 1.2.2_05a
Java 2 SDK 1.3.0 EA |
-Xms256m -Xmx512m
-server -Xms256m -Xmx512m |
| Test 8 |
Java 2 SDK 1.2.2_05a
Java 2 SDK 1.3.0 EA |
-Xms256m -Xmx512m
-Xgenconfig:64m,64m,semispaces:64m,512m,markcompact -Xoptimize
-server -XX:newSize=128m -XX:MaxNewSize=128m
-Xms256m -Xmx512m -XX:SurvivorRatio=2 |
| Test 9 |
Java 2 SDK 1.3.0 EA
Java 2 SDK 1.3.0 EA |
-server
-server -Xnoclassgc |
| Test 10 |
Java 2 SDK 1.3.0 EA
Java 2 SDK 1.3.0 EA |
-server
-server -Xnconcurrentio |
¹Runtime options that begin with -X are non-standard. They are
not guaranteed to be supported on all VM implementations, and are subject
to change without notice in subsequent releases of the Java 2 SDK.
²Options that begin with -XX are generally intended for Sun's internal
development use. Sun is not committed to supporting these options by fixing
any bugs noted with them; these options are subject to change without notice.
You use these options at your own risk.
³In this test the environment variable LD_LIBRARY_PATH was
set to /usr/lib/lwp
|
Test 1
In this test, HeapTest ran with:
- Java 2 SDK 1.2.2_05a with no runtime options specified.
- Java 2 SDK 1.2.2_05a with the runtime options
-Xms256m -Xmx512m -Xgenconfig:64m,64m,semispaces:64m,512m,markcompact -Xoptimize
| Option |
Meaning |
-Xms256m |
Set the initial size of the Java memory allocation pool (that is, the heap)
to 256 Mbytes |
-Xmx512m |
Set the maximum heap size to 512 Mbytes |
-Xgenconfig:64m,64m,semispaces:
64m,512m,markcompact |
Configure the heap for generational garbage collection, as follows:
Allocate a young generation of two 64 Mbytes semispaces (64m,64m,semispaces)
Allocate an initial object nursery space of 64 Mbytes, with a maximum memory
allocation of 512 Mbytes; mark-compact the old generation
(64m,512m,markcompact) |
-optimize |
Use the optimizing JIT compiler |
The results of the test are illustrated in the following graph:
|
|
Click to enlarge
|
The results indicate that the performance of an application that does
primarily memory-allocation tasks (marked in the graph as "heap-intensive")
or computation tasks (marked in the graph as "CPU-intensive")
improves if the heap is configured and the optimizing JIT compiler is specified.
The optimizing JIT compiler especially improves the performance of computation-intensive
code.
Note that a number of runtime specifications are pertinent to
generational garbage collection.
In generational garbage collection, all new objects are allocated
from a "nursery" (also known as the eden space or young space).
All the objects in the nursery constitute a "young generation" of objects.
When the nursery is full, the garbage collector does a partial
garbage collection. It reclaims memory in the nursery for objects
that are no longer accessible, that is, "dead" objects.
Objects in the nursery that are still "live" get moved to an area of
memory for older objects (the "old generation").
Generational garbage collection can be much faster than full garbage collection
because the garbage collector does not have to search all of memory for dead objects.
The runtime specification
-Xms256m -Xmx512m -Xgenconfig:64m,64m,semispaces:64m,512m,markcompact -Xoptimize
results in an initial heap size of 256 Mbytes. Of that 256 Mbytes, 128 Mbytes
are allocated for the nursery. The nursery is subdivided into two
64 Mbyte "semispaces." When the youngest semispace is full, the garbage
collector reclaims memory in the semispace, and moves any live objects in the semispace
to the other semispace. When the older semispace is full, the garbage collector
reclaims memory there, and moves any live objects to old-object memory.
The markcompact options specifies that when
old-object memory needs to be reclaimed, the garbage collector follows
a mark-compact algorithm. In this approach the garbage collector compacts gaps
left by dead objects in the "tree" of live objects.
Compacting gaps in the tree also tends to make object allocation faster.
Some things to notice:
- The graph displays a line labeled "linear scaling."
This is a trend line that plots the ideal slope in multithreaded performance.
In general, multithreaded code cannot improve on the downward slope of the
linear scaling line. Graphs for the other tests
in this article also show the linear scaling line.
- Performance got worse when the number of threads increased for the
default heap-intensive run. That's because as the number of threads
increased, the contention for the heap lock increased. The heap lock
is a monitor that controls synchronized access to the heap.
- Performance improved when the number of threads increased for the
default CPU-intensive run. This indicates that multi-threaded code
that is CPU-intensive executes better on a multi-processor system.
- There was a cross over in performance for the CPU-intensive and
heap-intensive runs using
-Xgenconfig and -Xoptimize.
That's because with fewer threads, there was less contention for the heap lock.
As the number of threads increased, the contention increased. This lead
to more time spent in allocation and garbage collection. As the
number of threads increased for the CPU-intensive runs, the program
was able to utilize the processors more efficiently.
Test 2
In this test, HeapTest ran with:
- Java 2 SDK 1.2.2_05a with no runtime options specified.
- Java 2 SDK 1.2.2_05a with the runtime options
-Xms256m -Xmx512m
| Option |
Meaning |
-Xms256m |
Set the initial size of the Java heap to 256 Mbytes |
-Xmx512m |
Set the maximum heap size to 512 Mbytes |
The results of the test are illustrated in the following graph:
|
|
Click to enlarge
|
The results indicate that the performance of an application that does
primarily heap-intensive or computation tasks improves if the heap is configured,
that is, by specifying at run time an initial and maximum heap size.
However the performance does not improve as much as it did in
Test 1 where the -Xgenconfig and -Xoptimize
runtime options were specified.
Test 3
In this test, HeapTest ran with:
- Java 2 SDK 1.2.2_05a with no runtime options specified.
- Java 2 SDK 1.3.0 EA with the runtime option
-server
| Option |
Meaning |
-server |
Run the JVM in server mode |
The results of the test are illustrated in the following graph:
|
|
Click to enlarge
|
When run with -server runtime option, the Java 2 SDK 1.3.0 EA
invokes the Java HotSpot Server VM (as opposed to the Java HotSpot Client VM).
The two virtual machines are essentially two different just-in-time compilers (JITs)
that interface with the same runtime system.
The results of this test appear to indicate that
Java 2 SDK 1.2.2_05a with default settings is more efficient than Java 2 SDK 1.3.0 EA
with the -server runtime option for applications doing
primarily heap-intensive tasks. However, Java 2 SDK 1_3_0b27 with the -server
runtime option is more efficient for applications doing primarily CPU-intensive tasks.
Note that the -server option in Java 2 SDK 1.3.0 is equivalent to
the -Xoptimize option in Java 2 SDK 1.2.2. The -server
option is recommended for all server-side applications, and is
especially useful in optimizing CPU-intensive code.
Test 4
In this test, HeapTest ran with:
- Java 2 SDK 1.2.2_05a with no runtime options specified.
- Java 2 SDK 1.3.0 EA with the runtime option
-server
| Option |
Meaning |
-server |
Run the JVM in server mode |
In addition, the environment variable LD_LIBRARY_PATH
was set to /usr/lib/lwp
The results of the test are illustrated in the following graph:
|
|
Click to enlarge
|
The LD_LIBRARY_PATH environment variable specifies the shared
libraries path. Setting it to /usr/lib/lwp forces the JVM to use
a one-level thread model. In a one-level thread model, each thread is associated
on a one-to-one basis with the Solaris kernel thread, or Solaris lightweight process (LWP).
By default, the JVM uses a two-level model, where threads are multiplexed
over possibly fewer LWPs. Using a one-level thread model can produce better
throughput for applications because it results in less contention on synchronization objects.
Comparing the results of this test to the results of Test 3,
it does not appear that setting LD_LIBRARY_PATH to
/usr/lib/lwp had a significant impact on performance.
Test 5
In this test, HeapTest ran with:
- Java 2 SDK 1.3.0 EA with the runtime option
-client
| Option |
Meaning |
-client |
Run the JVM in client mode |
- Java 2 SDK 1.3.0 EA with the runtime option
-server
| Option |
Meaning |
-server |
Run the JVM in server mode |
The results of the test are illustrated in the following graph:
|
| Click to enlarge |
When run with -client runtime option, the Java 2 SDK 1.3.0 EA
invokes the Java HotSpot Client VM. This VM is optimal for applications that need
to start up quickly or that require a small memory footprint.
When run with -server runtime option, the Java 2 SDK 1.3.0 EA
invokes the Java HotSpot Server VM. This VM is optimal for applications that require
high performance.
Test 6
In this test, HeapTest ran with:
- Java 2 SDK 1.3.0 EA with the runtime option
-server
| Option |
Meaning |
-server |
Run the JVM in server mode |
- Java 2 SDK 1.3.0 EA with the runtime options
-server -XX+UseBoundThreads
| Option |
Meaning |
-server |
Run the JVM in server mode |
-XX:+UseBoundThreads |
Bind user threads to Solaris kernel threads |
The results of the test are illustrated in the following graph:
|
|
Click to enlarge
|
The runtime option -XX:+UseBoundThreads runtime option binds each thread
to a Solaris kernel thread using the default thread library. In cases where an
application uses few threads, the -XX:+UseBoundThreads runtime option
can help alleviate thread "starvation," a condition where threads don't
progress because they don't get enough processor time.
The results of this test do not indicate any difference in the performance
of HeapTest when run with or without the -XX:+UseBoundThreads
runtime option.
Test 7
In this test, HeapTest ran with:
- Java 2 SDK 1.2.2_05a with the runtime options
-Xms256m -Xmx512m
| Option |
Meaning |
-Xms256m |
Set the initial size of the Java heap to 256 Mbytes |
-Xmx512m |
Set the maximum heap size to 512 Mbytes |
- Java 2 SDK 1.3.0 EA with the runtime options
-server -Xms256m -Xmx512m
| Option |
Meaning |
-server |
Run the JVM in server mode |
-Xms256m |
Set the initial size of the Java heap to 256 Mbytes |
-Xmx512m |
Set the maximum heap size to 512 Mbytes |
In addition, the environment variable LD_LIBRARY_PATH
was set to /usr/lib/lwp
The results of the test are illustrated in the following graph:
|
|
Click to enlarge
|
Comparing the results of this test to the results of Test 4,
it appears that with Java 2 SDK 1.3.0 EA, setting the initial and maximum heap size
values along with LD_LIBRARY_PATH=/usr/lib/lwp, can improve the performance
of an application performing heap-intensive tasks. For applications with fewer threads,
the alternate thread library gives better throughput. As mentioned in
Test 4, /usr/lib/lwp forces the JVM to use a
one-level thread model. This model results in less contention on
synchronization objects.
Test 8
In this test, HeapTest ran with:
- Java 2 SDK 1.2.2_05a with the runtime options
-Xms256m -Xmx512m -Xgenconfig:64m,64m,semispaces:64m,512m,markcompact -optimize
| Option |
Meaning |
-Xms256m |
Set the initial size of the Java heap to 256 Mbytes |
-Xmx512m |
Set the maximum heap size to 512 Mbytes |
-Xgenconfig:64m,64m,semispaces:
64m,512m,markcompact |
Configure the heap for generational garbage collection, as follows:
Allocate a young generation of two 64 Mbytes semispaces (64m,64m,semispaces)
Allocate an initial object nursery space of 64 Mbytes, with a maximum memory
allocation of 512 Mbytes; mark-compact the old generation
(64m,512m,markcompact) |
-optimize |
Use the optimizing JIT compiler |
- Java 2 SDK 1.3.0 EA with the runtime options
-server -XX:newSize=128m -XX:MaxNewSize=128m
-Xms256m -Xmx512m -XX:SurvivorRatio=2
| Option |
Meaning |
-server |
Run the JVM in server mode |
-XX:newSize=128m |
Set the default size for new generation to 128 Mbytes |
-XX:MaxNewSize=128m |
Set the maximum size for new generation to 128 Mbytes |
-Xms256m |
Set the initial size of the Java heap to 256 Mbytes |
-Xmx512m |
Set the maximum heap size to 512 Mbytes |
-XX:SurvivorRatio=2 |
Set the ratio of nursery-to-survivor space to 2 |
In addition, the environment variable LD_LIBRARY_PATH
was set to /usr/lib/lwp
The results of the test are illustrated in the following graph:
|
|
Click to enlarge
|
In this test, HeapTest ran with the same Java 2 SDK 1.2.2_05a
configuration settings as used in Test 1. It then ran
against an equivalent Java 2 SDK 1.3.0 EA configuration. In addition, the environment
variable LD_LIBRARY_PATH was set to /usr/lib/lwp.
The -XX:NewSize=128m and -XX:MaxNewSize=128m specifications
set the initial nursery size to 128 Mbytes, and the maximum nursery size to 128 Mbytes,
respectively. The -XX:SurvivorRatio=2 specification sets the ratio of
nursery space to "survivor" space to 2. Java 2 SDK 1_3_0b27 uses three spaces
for young objects, an eden space and two semispaces.
All new objects are initially allocated in the eden space. During garbage collection,
surviving objects are moved to a semispace.
The "survivor space" is the combined size of the two semispaces.
This means that for 128 Mbyte nursery, and a survivor ratio of 2, the
survivor space is 64 Mbytes and the other two semispaces are 32 Mbytes each.
The eden space is 64 Mbytes, that is, the difference between
the nursery size and the survivor space.
The results of the test indicate a slight improvement in performance of
HeapTest when performing heap-intensive tasks with Java 2 SDK 1_3_0b27
and the accompanying VM options and LD_LIBRARY_PATH settings, as compared to
performing those tasks with Java 2 SDK 1.2.2_05a and comparable settings.
Test 9
In this test, HeapTest ran with:
- Java 2 SDK 1.3.0 EA with the runtime option
-server
| Option |
Meaning |
-server |
Run the JVM in server mode |
- Java 2 SDK 1.3.0 EA with the runtime option
-server -Xnoclassgc
| Option |
Meaning |
-server |
Run the JVM in server mode |
-Xnoclassgc |
Prevent class unloading during full garbage collection |
The results of the test are illustrated in the following graph:
|
|
Click to enlarge
|
This test examined the impact of disabling class garbage collection
on the performance of an application running with Java 2 SDK 1.3.0 EA
and the Java HotSpot Server VM. The runtime option -Xnoclassgc
disables unloading of unreferenced classes during a full garbage collection.
The results indicate that specifying -Xnoclassgc
has no impact on performance.
Test 10
In this test, HeapTest ran with:
- Java 2 SDK 1.3.0 EA with the runtime option
-server
| Option |
Meaning |
-server |
Run the JVM in server mode |
- Java 2 SDK 1.3.0 EA with the runtime option
-server -Xconcurrentio
| Option |
Meaning |
-server |
Run the JVM in server mode |
-Xconcurrentio |
Set the synchronization implementation for
concurrent Input/Output |
The results of the test are illustrated in the following graph:
|
|
Click to enlarge
|
This test examined the impact of the Java 2 SDK 1.3.0 EA runtime option
-Xconcurrentio on performance. Specifying -Xconcurrentio
changes the implementation of thread synchronization in a way that
sometimes improves throughput for programs having many threads blocking Input/Output.
The gains are due to a higher level of concurrency in the Solaris operating system
kernel.
The results indicate that for HeapTest, specifying
-Xconcurrentio had a slightly positive impact on the performance
of heap-intensive tasks. Specifying -Xconcurrentio did not
improve the performance of CPU-intensive tasks.
Conclusion
The runtime option settings that produced the best performance results
for HeapTest are:
| Java 2 SDK Production Release |
Runtime Options |
| Java 2 SDK 1.2.2_05a |
-Xms256m -Xmx512m -Xgenconfig:64m,64m,semispaces:64m,512m,markcompact
-Xoptimize |
| Java 2 SDK 1.3.0 EA (heap-intensive) |
-server -XX:newSize=128m -XX:MaxNewSize=128m
-Xms256m -Xmx512m -XX:SurvivorRatio=2 |
| Java 2 SDK 1.3.0 EA (CPU-intensive) |
-server |
These best performance results are illustrated in the following graph:
 |
| Click to enlarge |
Specifying -Xgenconfig and -Xoptimize yields in the best performance
for both heap-intensive and CPU-intensive tasks with Java 2 SDK 1.2.2_05a.
Specifying SurvivorRatio yields the best performance for heap-intensive
tasks with Java 2 SDK 1.3.0 EA.
Specifying -server yields the best performance for
CPU-intensive tasks with Java 2 SDK 1.3.0 EA.
For More Information
Java
HotSpot VM Options.
Frequently Asked Questions About
The Java HotSpot Virtual Machine.
The Java HotSpot Performance Engine: An In-Depth Look.
Ed Ort is a staff member of the Java Developer Connection.
He has written extensively about relational database technology and programming languages.
Have a question about programming?
Use Java Online Support.
1 As used on this web site, the terms Java virtual
machine or Java VM mean a virtual machine for the Java platform.
|