The Guide to Solving Permgen Leaks

What is a Permgen Leak?

A java.lang.OutOfMemoryError: PermGen signals that you have a Permanent Generation leak in your application. This, in turn, means that whenever you redeploy an application in your application server it leaves a whole lot of classes behind. These old class definitions end up in your JVM permanent generation, eating up precious memory. Depending on your application it might take anything from one to tens of redeploys, but eventually your server will run out of permanent generation and crash.

Why is it important to fix it?

It is a memory leak. It consumes your precious memory you could put into better use. In development and/or test environments it mostly just means that you have to restart your application servers every once in awhile. So in these environments you just lose time, waiting for the redeploys to finish.

In production systems things are a lot worse – you’ll need to account for the risk that every redeploy could literally kill your production application. So you are forced to do full JVM restarts with every new release instead of just redeploying the app. This takes more time and your end-users could face lost sessions and/or service outages. Also – the production systems tend to run on quite expensive infrastructure so it might not be financially reasonable to spend a large amount of precious memory referencing to garbage.

How to fix the leak using the information provided by Plumbr?

As is also the case with other types of memory leaks, there is no golden tool or a fixed how-to list that you can follow to magically solve the problem. There are many possible cases for why the problem occurs and consequently many ways to solve it. Below we will present two typical examples of PermGen leaks along with possible ways to fix them.

Dangled class

There is a demo application shipped with Plumbr – a Spring PetClinic application that is a slightly modified by us to contain some memory leaks. Here is what Plumbr reports after a redeploy:

Permgen leak

As we see, the report highlights the org.hsqldb.jdbcDriver class, which is held for some reason in java.sql.DriverManager. The PetClinic application uses an HSQL database so when the application is started it loads the corresponding JDBC driver org.hsqldb.jdbcDriver. This class, being a good JDBC driver, registers itself with java.sql.DriverManager during initialization as required per JDBC specification. This registration includes storing a reference to an instance of org.hsqldb.jdbcDriver inside a static field of DriverManager.

Now, when the application is undeployed from the application server, java.sql.DriverManager will still hold that reference as there is no code in the HSQLDB library, the Spring framework or in the PetClinic application to remove it! As a result, a jdbcDriver object holds a reference to org.hsqldb.jdbcDriver class which in turn holds reference to the instance of java.lang.Classloader used to load the application. And that still references all classes of the application. In case of this particular demo application, almost 2000 classes are loaded during application startup. These occupy roughly 10MB in PermGen. Which means that it would take about 5-10 redeploys to fill PermGen with default size and get the “java.lang.OutOfMemoryError: PermGen space” crash.

In order to fix this problem the following servlet context listener can be used:

 //imports skipped
 public class DriverCleanup implements javax.servlet.ServletContextListener {

    // On application shutdown
    public void contextDestroyed(ServletContextEvent event) {
       Enumeration<Driver> drivers = DriverManager.getDrivers();
       for (; drivers.hasMoreElements();) {
          Driver driver = drivers.nextElement();
          // We search for driver that was loaded by this web application
          if (driver.getClass().getClassLoader() == this.getClass().getClassLoader()) {
             try {
                DriverManager.deregisterDriver(driver);
             } catch (SQLException e) {
                e.printStackTrace();
             }
          }
       }
    }

    public void contextInitialized(ServletContextEvent event) {
       // Nothing to do here
    }   
 }

This Servet listener should be registered in the web.xml file of your application:

 <listener>
    <listener-class>org.springframework.samples.petclinic.web.DriverCleanup</listener-class>
 </listener>

Leaking Threads

Another possible scenario for a classloader leak is through long running threads. This can (and will) happen when your application or, as was usually the case in my experience, a 3rd party library used by your application starts a long-running thread, e.g. a timer thread whose job is to execute some code periodically or a file system monitoring thread. It does not make any difference whether the thread was created directly via a new Thread(), or utilized some java.util.concurrent.ExecutorService. If the intended lifespan of that thread is not fixed, we would have a delicate situation. I mean: “You become forever responsible for what you have coded”. If any part of your application ever starts a thread you must make sure that it is not going to outlive the application.

Otherwise, if some thread continues to run after the application is undeployed, it will usually hold a reference to the classloader of the web application it was started by, the so-called context classloader. This means that all classes of the undeployed application continue to be held in memory. Remedy? If it is your application that starts new threads then you should shut them down during undeployment using a servlet context listener. If it is a 3rd party library starting the threads then you should search for its own specific shutdown hook. Or file a bug report if there is none.

Conclusion

There are other possible reasons that could cause your application encounter a java.lang.OutOfMemoryError: PermGen space. The root cause for the majority of them is some reference to an object or class loaded by the already dead application’s class loader or a direct link to the class loader itself. The remedy is quite similar for all of them. Firstly, find out where that reference is being held (Plumbr shows you this since version 1.1). Secondly, add a shutdown hook to your web application to remove this reference during the application’s undeploy. You can do that either by using a servlet context listener or by using an API provided by your 3rd party library.