We have been experiencing a PermGen out-of-memory problem in an application we have been working on when redeploying our .war to a Tomcat 7.0.33 application server. This would cause Tomcat to become unresponsive and crash after about 4 or 5 redeploys.
Our application is using Spring (Core, MVC, Security), Hibernate, and JPA. Our database pool is being maintained by Tomcat and we get connections to this pool via JNDI.
When redeploying our .war by copying it to $CATALINA_HOME/webapps/ we would see a few lines in our logs that looked something like this:
SEVERE: The web application [/myApp] created a ThreadLocal with key of type [java.lang.ThreadLocal] (value [java.lang.ThreadLocal@d73b31]) and a value of type [java.lang.Class] (value [class oracle.sql.TypeDescriptorFactory]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
So in our case it seemed to be an issue with the Oracle drivers. Tomcat 7 does a bit better of a job determining whether there have been leaks and reporting the problem than 6 so there may or may not be useful info in the logs of an older Tomcat install.
The PermGen memory space is typically a small memory space that the JVM uses to store class information and meta data. It is different from the heap in that your application code has no real control over its contents. When you start an application all the classes that are loaded will store information in the PermGen space. When your application is undeployed this space will be reclaimed.
The cause of the problem of running out of PermGen space when re-deploying a web app in general is that some references to classes can’t be destroyed by the JVM when the application is unregistered. This leaves all that classloader information sitting in PermGen since there are still live references. This can be caused by threads started by the application that aren’t cleaned up, database connections that are not cleaned up, etc. Sometimes this can be in libraries in use.
A good article on this can be found here:
HOWEVER, In our case this was caused by the way our application was deployed. Maven was configured with ojdbc6.jar as a dependency thusly:
This caused our war to include ojdb6.jar when deployed. So in WEB-INF/lib we would see “ojdbc6.jar” in our deployed application. This is where the problem starts. When our code requests a database connection it is creating a DataSource object using our *local* ojdbc6.jar (actually our local ClassLoader which uses our local .jar) rather than the one installed in Tomcat’s shared library directory (via Tomcat’s own ClassLoader). This now creates an object that Tomcat itself will hold on to but references code in our application and thus our ClassLoader. Now when our application is destroyed we get a leak since our ClassLoader can’t be removed.
In this scenario the solution is to tell Maven that the oracle library will be provided by our container. It will be available at compile-time for our code but will NOT be included in our .war.
Now when our application requests a DataSource the server ojdbc6.jar will be used removing the dependency on our application. This allows Tomcat to properly cleanup after “myApp.war” when it is removed.