Sunday, 19 August 2012

Managing PermGen and Heap allocations in Tomcat7

If you are developing a web-application that runs on Tomcat 7, it is highly likely that you will encounter a PermGen space error on Tomcat which happens when you deploy and undeploy the application several times and there is a memory leak. There could be several reasons for a memory leak and they could stem from loading third-party jars such as JDBC drivers and logger jars from within the web-application lib. To overcome some of these potential issues, it is best to load them directly into Tomcat lib and let Tomcat the Driver Manager take care of de-registering them when not in use.

While the best approach to dealing with this problems and other memory related problems is debugging the cause of the memory leak and fixing it, there are times when tuning the JVM parameters can alleviate the cause and improve performance of the application to a certain extent.

Before diving into the tuning the JVM parameters, it is important to understand how JVM organises its memory space and what is the difference between heap and permgen space and how they are used. This article gives a very good overview of the JVM memory layout.  To summarise the difference between the two, the permgen space is used to store classes loaded by the class loader, primitives,any static classes and other JVM related data. On the other hand, the heap space is mainly used to store objects created by the application. It is further divided as explained here into Eden, Survivor and Tenured Gen space.

It is also important to note that since Java 1.6 update 16, there have been some major changes to the process used to compute heap size in 32 bit and 64 bit JVMs and in client / server mode.

Having understood the basics of JVM memory organisation, we can look at tuning Tomcat's memory allocation for its heap and permgen. While there are several parameters that can be used for tuning the JVM, this post is just going to describe the minimum and maximum parameters for specifying the heap and the permgen space. The location of where to specify these parameters and their values depends on your machine specification (operating system, memory size, 32 bit or 64 bit) and how you invoke Tomcat.

Deciding on the actual values of the parameters should be taken after monitoring the JVM using some tools such as jVisualVM. The values given below are sample values that worked in my particular case.

Case 1: Tomcat is invoked from within Eclipse on a 32 bit machine.
Specs : Tomcat 7.0.28, Eclipse Juno, Sun Java 1.6.30, Windows 7.0

The parameters should be specified in Server Launch Configuration -> Open Launch Configuration -> Edit Launch Configuration Properties ->Arguments
Tomcat Launch Configuration in Eclipse
The detailed steps are available here.

Case 2: Tomcat is invoked via its start up script on a 32 bit virtual machine.
Specs : Tomcat 7.0.28, Sun Java 1.6.30, Ubuntu 12.04

Create a setenv.sh file and add the following one line in it.

export JAVA_OPTS="-Dfile.encoding=UTF-8 -server -Xms128m -Xmx256m -XX:MaxPermSize=128m -XX:+DisableExplicitGC"

Transfer this file to your Tomcat/bin directory and re-start Tomcat.

There are a number of ways of verifying that the JVM parameters were applied to Tomcat. The easiest way on Ubuntu is : ps -ef | grep tomcat

You should see something similar to the line below:

john     17841     1  5 15:23 pts/0    00:00:09 /usr/bin/java -


Djava.util.logging.config.file=/usr/local/apache-tomcat/conf/logging.properties -Xms128m -Xmx256m -XX:MaxPermSize=128m -XX:+DisableExplicitGC -
Djava.endorsed.dirs=/usr/local/apache-tomcat/endorsed -classpath /usr/local/apache-tomcat/bin/bootstrap.jar:/usr/local/apache-tomcat/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/apache-tomcat -Dcatalina.home=/usr/local/apache-tomcat -Djava.io.tmpdir=/usr/local/apache-tomcat/temp org.apache.catalina.startup.Bootstrap start



With respect to parameter values for the heap, it is important to note that :

(Managed Heap + native heap + (thread stack size * number of threads)) cannot exceed 2GB on 32bit x86 Windows or Linux systems (ref)


2GB is a theoretical limit. Since the heap requires contiguous memory space, it might be hard to push the heap beyond 1.4GB on a 32 bit Windows machine.

A final comment.
The JVM is an intelligent software and has several inbuilt processes built into it that work to optimize its performance. Attempting to tune the JVM may not always result in a performance improvement. Tuning the JVM is dependent on the machine specification, the design decisions taken behind the architecture of the web-application it is hosting, the number of applications hosted by the JVM and when all else fails, Trial and Error. 

No comments: