java.lang.OutOfMemoryError: Permgen space

Java applications are allowed to use only a limited amount of memory. The exact amount of memory your particular application can use is specified during application startup. To make things more complex, Java memory is separated into different regions as seen on the following diagram:

java.lang.outofmemoryerror: Permgen space

The size of all those regions, including permgen area, is set during the JVM launch. If you do not set the sizes yourself, platform-specific defaults will be used.

So – the “java.lang.OutOfMemoryError: PermGen space” message indicates that the Permanent Size area in memory is exhausted.

The cause

To understand the cause for the “java.lang.OutOfMemoryError: PermGen space“, we would need to understand what this specific memory area being exhausted contains.

For practical purposes, the permanent generation consists mostly of class declarations loaded and stored into PermGen. This includes the name and fields of the class, methods with the method bytecode, constant pool information, object arrays and type arrays associated with a class and Just In Time compiler optimizations.

As you can see from it, the PermGen size requirements depend thus both upon the number of classes loaded as well as the size of such class declarations. So it is easy to see the main cause for the “java.lang.OutOfMemoryError: PermGen space“: either too many classes or too big classes are being loaded to the permanent generation.

Examples

Minimalistic example

As seen in the causation section, PermGen space usage is strongly correlated with the number of classes loaded into the JVM. The following code serves as the most straightforward example:

import javassist.ClassPool;
            public class MicroGenerator {
            public static void main(String[] args) throws Exception {
            for (int i = 0; i < 100_000_000; i++) {
            generate("eu.plumbr.demo.Generated" + i);
            }
            }
            public static Class generate(String name) throws Exception {
            ClassPool pool = ClassPool.getDefault();
            return pool.makeClass(name).toClass();
            }
            }

In this example the source code is iterating over a loop and generating classes at the runtime. Class generation complexity is taken care by the javassist library.

Launching the code above will keep generating new classes and loading their definitions in Permgen space until the space is fully utilized and the “java.lang.OutOfMemoryError: Permgen space” is thrown.

Redeploy-time example

As bit more complex and more realistic example, lets walk you through a “java.lang.OutOfMemoryError: Permgen space” error occurring during the application redeploy. When you are redeploying an application you are trying to get rid of the previous classloader referencing all the previously loaded classes and replace it with a classloader loading new versions of the classes.

Unfortunately many 3rd party libraries and poor handling of resources such as threads, JDBC drivers or filesystem handles makes unloading the previously used classloader impossible. This in turn means that during each redeploy all the previous versions of your classes will still reside in PermGen generating tens of megabytes of garbage during each redeploy.

The example application at hand is connecting to a relational database using JDBC drivers. When the application is started, the initializing code loads the JDBC driver to connect to the database. Corresponding to specification, this JDBC driver registers itself with java.sql.DriverManager. This registration includes storing a reference to an instance of the driver 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 a result we have a live reference the driver class which in turn holds reference to the instance of java.lang.Classloader used to load the application.

And that instance of java.lang.ClassLoader still references all classes of the application, usually occupying tens of megabytes in PermGen. Which means that it would take just a handful of redeploys to fill a typically sized PermGen and get the “java.lang.OutOfMemoryError: PermGen space” error message in your logs.

The solution

1. Solving initialization-time OutOfMemoryError

When the OutOfMemoryError due to PermGen exhaustion is triggered during the application launch, the solution is simple. The application just needs more room to load all the classes to the PermGen area so we just need to increase its size. To do so, alter your application launch configuration and add (or increase if present) the -XX:MaxPermSize parameter similar to the following example:

java -XX:MaxPermSize=512m com.yourcompany.YourClass

In the example above, the JVM that PermGen is allowed to grow up to 512MB before complaining in the form of OutOfMemoryError.

2. Solving redeploy-time OutOfMemoryError

When the OutOfMemoryError occurs right after you redeploy the application, you are facing a classloader leakage. In such case the easiest and most straightforward way to solve the problem is to grab a 14-day free trial on Plumbr, find the offending code and solve it in minutes.

The alternatives for those who cannot use Plumbr or decide otherwise is also present. For this, you should proceed with heap dump analysis – taking the heap dump after a redeploy similar to following example

jmap -dump:format=b,file=dump.hprof <process-id>

open the dump with your favourite heap dump analyzer (Eclipse MAT is a good tool for that). In the analyzer, you can look for duplicate classes, especially those loading your application classes. From there, you need to progress to all classloaders to find the currently active classloader.

For the inactive classloaders, you need to determine the reference blocking them from being GCd via harvesting the shortest path to GC root from the inactive classloaders. Equipped with this information you now have the root cause. In case the root cause was in 3rd party library, you can proceed to Google/StackOverflow to see if this is a known issue to get a patch/workaround. If this was your own code, you need to get rid of the offending reference.

3. Solving run-time OutOfMemoryError

When the application is running out of PermGen memory during runtime, the Plumbr dynamic leak detection capability is the best way to find the source for the leakage. Grab the free 14-day trial and get rid of the issue.

The alternatives for those who cannot use Plumbr is also present. First step in such case is to check whether the GC is allowed to unload classes from PermGen. The standard JVM is rather conservative in this regard – Classes are born to live forever. So once loaded, classes stay in memory even if no one is really using them anymore. This can become a problem when the application is creating lots of classes dynamically and the generated classes are not needed for longer periods. In such a case, allowing JVM to unload class definitions can be helpful. This is achieved by adding again just one configuration parameter to your startup scripts:

-XX:+CMSClassUnloadingEnabled

By default this is set to false and so to enable this you need to explicitly set the following option in Java options. If you enable CMSClassUnloadingEnabled, GC will sweep PermGen too and remove classes which are no longer used. Keep in mind that this option will work only when UseConcMarkSweepGC is also enabled using the below option. So when running parallel or, God forbid, serial GCs, make sure you have set your GC to CMS by specifying:

-XX:+UseConcMarkSweepGC

After making sure classes can be unloaded and the issue still persists, you should proceed with heap dump analysis – taking the heap dump similar to following example

jmap -dump:file=dump.hprof,format=b <process-id>

open the dump with your favourite heap dump analyzer (Eclipse MAT is a good tool for that) and progressing find the most expensive classloaders by the number of classes loaded. From such classloaders, you can proceed to extract the loaded classes and sort such classes by the instances to have the top list of suspects.

For each suspect, you now need to manually trace the root cause back to your application code potentially generating such classes.