Understanding and avoiding the Java Permgen Space error
In this article, I look into what it means when a Java program runs into a OutOfMemoryError: PermGen Space error. I first explain what the permanent generation heap space is, after which I explain the usual cause of the Permgen Space error and I give some pointers on how to avoid it.
Usually, we do not look into JVM intrinsics. We take the JVM as is.
Some of the world’s finest engineers are working on the JVM(s) and I am sure we can’t improve their work, definitely not without getting seriously involved.
However, as a Java developer, you are often confronted with performance issues, usually working memory related.
The problem I run into most is the dreaded OutOfMemoryError: PermGen Space error. I thought it would be nice to know more about it, so I looked into what causes it exactly.
Java memory structure
To understand the error, we have to look into how the jvm memory is structured.
There are two memory regions in the JVM: the heap and the stack. Local variables and methods reside on the stack, everything else on the heap.
This Java heap memory is structured again into regions, called generations. The longer an object lives, the higher the chance it will be promoted to an older generation. Young generations(such as Eden on Sun JVM) are more garbage collected than older generations(survivor and tenured on Sun JVM). However, there is also some separate heap space called permanent generation. Since it is a separate region, it is not considered part of the Java Heap space. Objects in this space are relatively permanent. Class definitions are stored here, as are static instances.
Without getting into details, Classloaders deploy and undeploy classes all the time. For example, this happens when an application is deployed or undeployed on a webserver. On web servers, all applications have their own Classloader. When an application is deployed or undeployed, its class definitions and Classloader are respectively put into and removed from the permanent generation heap.
OutOfMemoryError: PermGen Space
The OutOfMemoryError: PermGen Space error occurs when the permanent generation heap is full. Although this error can occur in normal circumstances, usually, this error is caused by a memory leak.
In short, such a memory leak means that a classloader and its classes cannot be garbage collected after they have been undeployed/discarded.
To give an example on how this can happen, let’s say we have a Payment class, which is part of a jar in a web application that is deployed on some webserver. In the lib folder of the web server, there is some logging framework present, which has a Log class with the method register(Class clazz) with which classes can be registered for logging. Let’s say that the Payment class gets registered by this method and the Log class starts keeping a reference to the clazz object. When the Payment class gets undeployed, it is still registered with the Log class. The Log class will still have a reference to it and hence, it will never be garbage collected. Moreover, since the Payment Class has a reference to its ClassLoader in turn, the ClassLoader itself will never be garbage collected either, and so will none of the classes it loaded.
An even more typical example is with the use of proxy objects. Spring and Hibernate often make proxies of certain classes. Such proxy classes are loaded by a classloader as well, and often, the generated class definitions – which are loaded like classes and stored in permanent generation heap space – are never discarded, which causes the permanent generation heap space to fill up.
Avoiding the error
1. Increasing the maximum size of the permgen heap
The first thing one can do is to make the size of the permanent generation heap space bigger.
This cannot be done with the usual –Xms(set initial heap size) and –Xmx(set maximum heap size) JVM arguments, since as mentioned, the permanent generation heap space is entirely separate from the regular Java Heap space, and these arguments set the space for this regular Java heap space. However, there are similar arguments which can be used(at least with the Sun/OpenJDK jvms) to make the size of the permanent generation heap bigger:
would set its maximum size to 256m, which is 4 times bigger than the default size.
2. Use common sense when using static fields on classes.
Make sure you do not write classes that have static variables keeping references to class definitions and the like.
Using JDK dynamic proxies instead of cglib proxies
So using jdk dynamic proxies instead of cglib might be a good idea when getting the error.
Also, newer versions of Hibernate appear to not use cglib as a bytecode provider anymore, so upgrading your version of Hibernate, might drastically lower your chances on getting the error.
In general, when getting the error, one needs to determine why certain class definitions are not garbage collected. Once that is known, it should be possible to battle the error.