What is a PermGen Leak?
Join the DZone community and get the full member experience.Join For Free
First of all we go through the core concepts required to understand the subject - objects, classes, classloaders and JVM memory model. If you feel yourself familiar with the basic concepts, you can directly jump into the section where I will describe two typical cases for this error alongside with hints and suggestions for solving it.
Objects, Classes and ClassLoaders
Well, I will not start with the really basics. I guess if you have already found us, then you should be familiar with the concept that everything in Java is an Object. And that all Objects are specified by their Class. So every object has a reference to an instance of java.lang.Class describing the structure of this object’s class.
But what actually happens under the hood, when you create a new object in your code? For example if you have written something truly complicated like
Person boss = new Person()
Java Virtual Machine (JVM) needs to understand the structure of the object to create. To achieve this, JVM looks for the class called Person. And if the Person class is accessed for the first time during this particular execution of the program, it has to be loaded by JVM from (usually) the corresponding Person.class file. The process of seeking for the Person.class file on the drive, loading it into memory and parsing it’s structure is called class loading. And that is responsibility of a ClassLoader. ClassLoaders are instances of java.lang.ClassLoader class and each and every class in java program has to be loaded by some ClassLoader. As a result we have following relationships:
As you can see from the next diagram every classloader holds references to all the classes it has loaded. And for this article these relationships are far more interesting.
So, remember this diagram, we will need it later.
Almost every JVM nowadays uses separate region of memory, called Permanent Generation (or PermGen for short), to hold internal representations of java classes. PermGen is also used to store more, about what you can find out from this post, but for this article we stick only to the class definitions being stored in PermGen. The default size of this region on my two machines running java 1.6 is not very impressive 82MB.
As I have explained in one of my earlier posts, memory leak in Java is a situation where some objects are no longer used by an application, but Garbage Collector fails to recognize them as unused. Which can lead to OutOfMemoryError if those unused objects contribute to the heap usage significantly enough that next memory allocation request by the application cannot be fulfilled.
The root cause of java.lang.OutOfMemoryError: PermGen space is exactly the same: JVM needs to load the definition of new class but there is not enough space in PermGen for it - there are already too many classes over there. Which can be because your application or server uses that many classes that the current size of PermGen is not enough. Or it can be a memory leak.
Permanent Generation Leak
But still, how on earth it is possible to leak something in PermGen? It holds definitions of java classes and they cannot become unused, can they? But actually, they can. In case of java web application deployed into an application server all those classes in your EAR/WAR become worthless when application is undeployed. JVM continues to run, as application server is still alive, but a whole bunch of class definitions are not in use anymore. And they should be removed from PermGen. If not, we have memory leak in PermGen area.
As a nice sample on the reasons - Tomcat
developers have set up a Wiki page describing
different leaks found and fixed in the Apache Tomcat versions 6.0.24 and above.
One possible scenario for classloader leak is through long running threads. It happens when your application or, as usually was the case in my experience, 3rd party library used by your application, starts some long running thread. An example of this could be a timer thread whose job is to execute some code periodically.
If the intended lifespan of this thread is not fixed, we are now heading directly into a trouble. In case when any part of your application ever starts a thread, you must make certain, that it is not going to outlive the application. In typical cases the developer either is not aware of this responsibility or simply forgets to write the clean-up code.
Otherwise, if some thread continues to run after application is undeployed, it will, usually, hold a reference to a classloader of the web application it was started by, called context classloader. Which 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 servlet context listener. If it is 3rd party library, then you should search for its own specific shutdown hook. Or file a bug report if there is no one.
Another typical case of a leak can be caused by database drivers. We have encountered this leak in our own demo application that we ship with Plumbr. It is slightly modified Pet Clinic application shipped along with Spring MVC. Let us highlight some things that happen when this application is being deployed to the server.
- Server creates new instance of java.lang.Classloader and starts to load application’s classes using it.
- PetClinic uses HSQL database, so it loads corresponding JDBC driver, org.hsqldb.jdbcDriver
- This class, being good JDBC driver, registers itself with java.sql.DriverManager during initialization, as required per JDBC specification. This registration includes storing inside a static field of DriverManager a reference to an instance of org.hsqldb.jdbcDriver.
Now, when application is undeployed from the application server, java.sql.DriverManager will still hold that reference, as there is no code in HSQLDB library nor in the Spring framework nor in the application to remove that! So as was explained above, a jdbcDriver object still holds reference to org.hsqldb.jdbcDriver class, which in turns holds reference to the instance of java.lang.Classloader used to load the application. This classloader now still references all classes of the application. In case of our particular demo application, during application startup almost 2000 classes are loaded occupying roughly 10MB in PermGen. Which means that it takes about 5-10 redeploys to fill PermGen with default size to get java.lang.OutOfMemoryError: PermGen space crash.
How to fix that? One possibility is to write servlet context listener, which deregisters HSQLDB driver from DriverManager during application shutdown. This is pretty straightforward. But as of now a developer has to be aware of the problem and remember to write the corresponding code in each application.
Download our latest version of Plumbr with demo application and play with it to find out how the leak is occurring, how Plumbr finds it and how do we explain the cause.
There are a lot of reasons, why your application can encounter java.lang.OutOfMemoryError: PermGen space. The root cause for the majority of them is some reference to an object or class loaded by already dead application’s class loader. Or direct link to the class loader itself. The remedy is also quite similar for all of them. Firstly find out where this reference is being held. Secondly add a shutdown hook to your web application to remove this reference during application’s undeployment either using servlet context listener or by using API provided by your 3rd party library.
Finding those leaking references has never been easy. We ourselves have spent countless hours trying to trace down why some applications require 20MB of PermGen on each redeploy. But as of version 1.1, Plumbr will show you the cause of the leak and gives you a hint how to fix it. So go ahead, register and download Plumbr. If you are running an older version of Plumbr, we strongly recommend downloading an upgrade.
Published at DZone with permission of Nikita Salnikov-Tarnovski, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Integration Architecture Guiding Principles, A Reference
DevOps Pipeline and Its Essential Tools
What Is mTLS? How To Implement It With Istio
The Native Way To Configure Path Aliases in Frontend Projects