Daniel Mikusa, VMware SpringSource Software Support Engineer, has shared a series of articles intended to help Apache Tomcat administrators to performance tune the JVM to improve the performance of garbage collection. Garbage collection is necessary in order to free up memory, however excessive garbage collection can cause an application to become unresponsive. His first article oriented users on the goals of optimizing garbage collection and how to instrument the JVM to monitor performance. If you have not read the first article, I strongly suggest you do before continuing, so you fully understand the process of performance tuning.
This week’s article focuses on the JVM options as they are applied to Apache Tomcat. Please note that the usage of these options may not be suitable for other applications.
First, Mikusa walks us through the key configuration options we need to understand while performance tuning the JVM:
- -Xms and –Xmx. Used to define the size of the heap used by the JVM, -Xms sets the initial size of the heap and -Xmx sets the maximum size of the heap. Specific values depend on the application.For Tomcat, it is recommended that –xms and –xmx be set to the same value. Referred to as a fully committed heap, this will instruct the JVM to create a heap that is initially at its maximum size and prevent several full garbage collections from occurring as the heap expands to its maximum size. Remember, max heap equals 4 or 5 times the minimum heap.
- -XX:PermSize and -XX:MaxPermSize. Used to define the size of the permanent generation space,it is also recommended that these be set to the same value. This will instruct the JVM to create the permanent generation so that it is initially at its maximum size and prevent possible full garbage collections from occurring as the permanent generation expands to its maximum size.
- -Xss. Used to define the size of the stack for each thread in the JVM. The optimum value varies across applications however, in most cases, the default value used by the JVM is too large. Try lowering the value, starting with 128k, saving memory and increasing the number of threads that can be run on a system. Run Tomcat and look for a StackOverFlow exception in the logs. If you see the exception, then gradually increase the value and restart Tomcat. When the exceptions disappear, you have found the minimal value which works for your deployment.
- -server. Used to select the Java HotSpot Server VM when running 32-bit Windows. This will instruct the VM that it is running in a server environment and the default configurations will be changed accordingly. 32-bit Solaris and 32-bit Linux installations with two or more CPU's and 2GB or more of RAM will enable this option by default. In addition, all 64-bit OS's have this option enabled by default as there is no 64-bit client VM.
For most applications, tuning these options is sufficient to optimize your garbage collection. However, for larger applications or applications that consume a large amount of heap, you will also want to tune your garbage collector as well.
Tuning the Collector
Garbage collection is always a trade off between heap size, garbage collection pauses and throughput. An administrator’s rule of thumb is that you can usually optimize two at the expense of the third. Selecting a collector can help minimize the trade off depending on your application’s behavior. Mikusa explains:
The JVM ships with three commonly used collectors: the serial collector, the parallel collector and the concurrent collector. In most cases when running Tomcat, you'll be using either the parallel collector or the concurrent collector. The difference between the two being that the parallel collector typically offers the better throughput, while the concurrent collector often offers lower pause times.
The parallel collector can be enabled by adding -XX:+ UseParallelGC to CATALINA_OPTS or the concurrent collector can be enabled by adding -XX:+ UseConcMarkSweepGC to CATALINA_OPTS (you would never want to have both options enabled). As to which of the collectors you should be using, it is difficult to give a blanket recommendation. I would suggest that you give both a try, measure the results and use that to make your decision.
For the most part, you should run your collector with the default values. However, Mikusa does explain how to take tuning the collector one step further for two of the garbage collectors:
When you specify the option to run the parallel collector, it will only run on the young generation.This means that multiple threads will be used to process the young generation, but the old generation will continue to be processed by a single thread. To enable parallel compaction of the old generation space you can enable the option -XX:+ UseParallelOldGC. Note that this option will help the most when enabled on a system with many processors.
When you specify the option to run the concurrent collector, it is important to realize that garbage collection will happen concurrently with the application. This means that garbage collection will consume some of the processor resources that would have otherwise been available to the application. On systems with a large number of processors, this is typically not a problem.However, if your system has only one or two processors then you will likely want to enable the -XX:+ CMSIncrementalMode option. This option enables incremental mode for the collector, which instructs the collector to periodically yield the processor back to the application and essentially prevents the collector from running for too long.
For more background and detail on performance tuning your garbage collection, see the full article here on Tomcatexpert.com.