Previous Contents Index Next |
iPlanet Application Server Performance and Tuning Guide |
Chapter 5 Tuning the Java Runtime System
You can tune the Java Runtime system by binding the application threads to Solaris® user level threads. The Solaris operating environment, by default, supports a two level thread model. Application level Java threads are mapped to user level Solaris threads, which are multiplexed on a limited pool of light weight processes (lwps). Often, we need only as many lwps as there are processors on the system, leading to conserved kernel resources and greater system efficiency. This helps when there are hundreds of user level threads.In this chapter, we will discuss the following topic:
Using Bound Threads
Using Bound Threads
It is also possible to bind application threads to Solaris lwps, on a 1-1 basis. On some applications, better performance may be achieved by using the non-default model, when there are only a limited number of threads and hence few lwps being created. The JVM can be configured to map Java threads to bound Solaris threads before the KJS executable command is invoked in the KJS shell script:
Managing Memory and Allocation
The efficient running of any tool depends on how well memory and garbage collection is managed. The topics listed in this section will provide you with the information you need to optimize on memory and allocation functions.In this section, we will discuss the following topics:
Tuning the Garbage Collector
Tuning the Garbage Collector
Newer Java Runtime Environments (JRE) come with a generational object memory system and sophisticated garbage collection algorithms.A generational memory system, divides the heap into a few carefully sized partitions, called generations. The efficiency of a generational memory system is based on the observation that most objects are short lived. Newly allocated objects are allocated in the young generation (also referred to as the Eden). Because of the high mortality rates of newly allocated objects, scavenging or garbage collecting in the young generation is often very productive, quickly recycling a lot of allocation space.
Compacting garbage collectors use two semi-spaces in the eden, copying surviving objects from one young space to the second. Objects that survive multiple young space collections are tenured, that is, copied to a tenured generation. The tenured generation is larger and fills up less quickly. So it is garbage collected less frequently and each collection takes longer than a young space only collection. Collecting the tenured space is also referred to as doing a full GC.
The frequent young space collections are quick (few milliseconds) and the occasional full GC takes a relatively longer time (tens of milliseconds to even a few seconds, depending upon the heap size).
Other garbage collection algorithms such as the Train algorithm, are incremental, that is. they chop down the full GC into several incremental pieces. This provides a high probability of small garbage collection pauses even when full gc kicks in. This does come with an overhead and is usually not required for enterprise web applications.
Typically, a third generation, called the permanent generation is also created by the JVM to store internal objects such as loaded java classes.
Both HotSpot and Solaris JDK sport a generational garbage collection system. Only HotSpot ships with the incremental Train garbage collector. HotSpot is the default on both Solaris and NT platforms. In the future, new parallel and concurrent collectors will be introduced in JDK 1.4.
Both HotSpot and Solaris JDK use thread local object allocation pools for lock-free, fast, and scalable object allocation. User application level object pooling may have actually been beneficial when running on earlier generation Java Virtual Machines. But it might actually slow down the application, on this new generation virtual machines available with JDK 1.2 onwards. Consider pooling only if the object construction cost is very high and shows up being significant in the execution profiles.
Specifying Garbage Collector Setting
The following settings can be used to improve memory utilization by the Garbage Collector:
Explicit Garbage Collector
Memory utilization by the application server can now be controlled by modifying the behavior of the explicit Garbage Collector. You can enable or disable the cleaner by modifying the JAVA_GX_ARGS=-DGX.cleaner.enabled key in iasenv.ksh file on Solaris, and in the registry settings on Windows.On Solaris, the JAVA_GX_ARGS=-DGX.cleaner.enabled entry is by default set to no, to improve response time for requests. If you notice an inordinately high usage of memory by iPlanet Application Server, enable the cleaner by changing the value to yes, or by commenting out the JAVA_GX_ARGS line.
JAVA_GX_ARGS=-DGX.cleaner.enabled=yes
Once enabled, the cleaner is invoked every 10 seconds. The cleaner interval can be controlled by adding the following line to iasenv.ksh file:
JAVA_GX_ARGS=-DGX.cleaner.interval=N where N denotes the time in milliseconds.
On Windows, the cleaner is enabled by default. To improve performance you can disable the cleaner or set a longer time interval.
If you want to modify or change the default behavior of the cleaner, it can be done by adding the following flags to the JavaArgs in the registry under, SOFTWARE\iPlanet\Application Server\6.0\Java .
-DGX.cleaner.doGC=yes -DGX.cleaner.interval=N
where N denotes the time in milliseconds.
Deferred Garbage Collection
A new switch has been introduced to enable deferred GC for Applogic based applications that see memory growth. This can be turned on by setting the java system property useDeferredGC.On Solaris you can enable this by appending the following JAVA_ARGS in iasenv.ksh file:
On Windows, append the following to the JavaArgs entry which can be found in the registry under SOFTWARE\iPlanet\Application Server\6.0\Java :
The default value of this property is set as false, since high memory growth has been reported with a few Applogic based applications. In deferred garbage collection, the references of the newly created objects are stored temporarily till the end of execution of the request, to prevent its garbage collection. Therefore at the end of the request, all the memory is freed at once but it can also lead to transient (until end of request) memory growth.
Tracing Garbage Collection
Supplying -verbose:gc (sometimes -verbosegc) flag to the JVM will result in an informative one line message being printed out at every collection. It is useful to turn this flag on for a couple of reasons.
It gives a log of all the garbage collection pauses and their length to detect if there are unacceptably long pauses.
It serves as a "heartbeat" of sorts showing that the JVM is alive and well.
You can see if the application is leaking Java objects very easily. A memory leak is suspect if the number of non-garbage objects increases, even after many, full garbage collections.
- Note that it is possible to get normal garbage collection even if the application logic is deadlocked.
Tuning the Java Heap
Now that we know what generational garbage collection is about, it is easy why heap configuration helps performance.This section contains the following topics:
Guidelines for Java Heap Sizing
HotSpot Server VM Tuning Options
Guidelines for Java Heap Sizing
These are important guidelines for sizing Java heap.
Determine how much of Java heap you can afford to give each JVM process.
Example: -Xms64m -Xmx64m clamps heap size at 64m. Setting the starting heap size (-Xms) as well as the maximum allowed heap size (-Xmx) to the same value has a benefit. If the JVM were allowed to start with the default starting heap size, the heap size expands automatically. However, expansion is a slow process and during this heap expansion phase there would be frequent garbage collection and performance will be hampered.
Set the starting and maximum Java heap size to the size that you have determined above. The JVM flags, -Xms<size> and -Xmx<size> flags specify the minimum and maximum heap size. Look at the JVM documentation for more details.
- You can do this by first determining what is the amount of system memory that can be used by the application server node. Then divide it equally between the number of KJS processes that you wish to configure. Each KJS process is a JVM process.
- The rule of thumb for the ratio of KJS processes to the number of CPUs is one KJS per CPU. You could experiment with a little more or a little less.
Larger Eden or younger generation spaces increase the spacing between full garbage collections. But young space collections could take a proportionally longer time. In general, you could keep the eden size between 0.25 and 0.5 times the maximum heap size.
HotSpot Server VM Tuning Options
iPlanet Application Server 6.5 loads the 1.3 Hotspot Server VM by default. The Server mode VM is better suited for server side applications. iPlanet Application Server starts up with a tuned VM with the following arguments:-server -Xss512k -Xms128m -Xmx1024m -XX:NewSize=42m -XX:MaxNewSize=342m
-Xconcurrentio -XX:+DisableExplicitGC
You can increase or decrease the heap sizes based on the physical memory available.
These options may work well for some applications, whereas it might not for certain others. It depends on the type of application- whether its I/O bound, or compute intensive, or memory intensive, etc. You might have to experiment with the tunable parameters before deciding on the best options.
Table 5-1 gives the VM options and their descriptions.
Setting the young generation sizes to a constant value will prevent resizing. During our tests we found that using incremental GC reduces throughput.
For more information on JVM tuning, see http://java.sun.com/docs/hotspot/index.html.
Sample heap configuration on Solaris
Add the following arguments to the JAVA_ARGS environment variable in the KJS shell script:-Xgenconfig:64m,64m,semispaces:64m,512m,markcompact
This creates a 512 MB Java heap, with two 64 MB semi-spaces for the young generation. We specify that a mark and compact algorithm be used. It is possible to increase the size of the young generation and the overall heap size. The Solaris JDK appears to allocate twice the amount as the specified size of each semi space, with the justification of accounting for object header and other space overheads.
-Xgenconfig is a complex flag to understand and get right. It is not well publicized and documented except in JVM internal articles.
-Xgenconfig, -Xms, -Xmx flags obviously interact and when specified together the genconfig setting overrides other settings. However, some range checking seems to be performed by the JVM to make sure that the minimum and maximum are consistent with what is specified to genconfig.
Sample Heap configuration on Windows
You can pass arguments to the JVM by setting the following property in iPlanet Registry:HKEY_LOCAL_MACHINE\SOFTWARE\iPlanet\Application Server\6.0\Java\JavaArgs, to the desired string. This is where you set heap sizing parameters.
-Xms and -Xmx flags should be set as as described in the case of the Solaris JDK.
-XX:NewSize=<size> specifies the initial size, in bytes, of the young object space where new objects are allocated. The default initial young space size is 2MB. NewSize must be a multiple of 1024. Append the letter k or K to indicate kilobytes, or m or M to indicate megabyte. -XX:NewSize=64m sets the initial size of the young space to 64mb. Note that a large young space size may result in increased garbage collection pause times.
-XX:MaxNewSize=<size> specifies the maximum size, in bytes, of the young object space where new objects are allocated. The initial young space size is 2MB. MaxNewSize must be a multiple of 1024, and greater than 2MB. Append the letter k or K to indicate kilobytes, or m or M to indicate megabytes. The default value for MaxNewSize is 64 MB. -XX:MaxNewSize=128m allows the young space to expand, if needed, to 128 MB.
-XX:SurvivorRatio=k sets ratio of eden size to survivor space size. For example the default ratio of 8 on Windows, with NewSize=64m, results in two semi spaces, each 4mb in size. Using NewSize and SurvivorRatio it is possible to get the desired semi-space size.
We recommend that you try this flag as a last resort, as it can disturb quite a few of the internal sizing calculations.
These HotSpot flags could apply all HotSpot based Java Runtime Environments. HotSpot based JDK 1.3 is supplied as default on Solaris, Windows and Linux. All HotSpot performance flags are listed at: http://java.sun.com/docs/hotspot/VMOptions.html.
Tuning the Dynamic Compiler
Both Java HotSpot 1.3 for Windows and Solaris JDK 1.3.1 implement adaptive dynamic compilation, to detect program hotspots and compile only the hot program segments for peak performance. You are likely to observe a short ramp up, when this profile driven compilation takes place at application start up. For this reason be careful to make benchmark measurements on a warmed-up iPlanet Application Server.Turning off the JIT is expensive. We have measured up to three times hit in performance on a well tuned system configuration when the JIT is turned off while using an application. Your mileage may vary, depending on the nature of the application and system bottlenecks on your hardware and database configuration.
In the following cases, you may wish to turn off the Dynamic Compiler:
During debugging, to print and examine Java exception stack traces, annotated with source line numbers.
If you do have to turn off the compiler, on Windows, supply -Xint in JavaArgs property of registry. On Solaris, add -Djava.compiler=none to JAVA_ARGS in the KJS shell script.To work around some rare Dynamic compiler bugs. In this case there are hidden JVM arguments with which you can selectively disable compiling a particular method or all methods in a class. Contact your Sun Java support for details.
Previous Contents Index Next
Copyright © 2002 Sun Microsystems, Inc. All rights reserved.
Last Updated March 06, 2002