The Unknown Generation: Perm

The Hotspot Virtual Machine employs generational garbage collection: a young generationholds recently created objects, the tenured generation those objects which survived (multiple) major Garbage Collections (GCs). The heap dump contains all objects from both spaces, even though we cannot tell anymore to which generation they belonged.

The permanent generation (or: perm space) is a different beast: It is used to store class and method data as well as interned strings. Just like heap space, you can also run out of perm space. That’s what happens for example if you install too many plug-ins to your Eclipse IDE:OutOfMemoryError: PermGen full. In this case, you have to increase the available perm space using -XX:MaxPermSize.

Too many plug-ins are only one reason: One can run out of perm space due to too manyinterned Strings or because of leaking class loaders. Increasing perm space will only help for so long.

Because a heap dump does not really contain a lot of information about perm space, perm problems are difficult to tackle. Recently, I found this great article by Sporar, Sundararajan and Kieviet. The authors shed some light on the permanent generation. Of course, I had to check right away if and how I can use the Eclipse Memory Analyzer to analyze this “unknown” generation. This is what this blog is about.

If you want to learn more about the different garbage collection algorithms, check out Sun’s
Virtual Machine Garbage Collection Tuning Guide.

Getting an Overview

So what does the Memory Analyzer tell you about this unknown generation? For a start, the overview shows the total number of classes loaded and the total number of class loaders:

Heap Overview with Total Number of Classes and Class Loaders

Use the Java Basics -> Class Loader Explorer to drill-down…

Pick the Class Loader Explorer

… and list class loader details:

Class Loader Explorer

The first column contains the class loader names. If available, the Memory Analyzer prints the name of the component that was loaded by that class loader. In case of an Eclipse dump that is the symbolic name of the plug-in. In case of a Web Application Archive (WAR) deployed to a Jetty web container this could be the context path. One can add more such name resolvers via extensions. This is one of the most important features of the Memory Analyzer: make sense of the data in a heap dump by adding application specific knowledge. In the future, we plan to make the name resolver configurable via a preference page.

The second column shows the number of defined classes. What’s the difference betweendefined and loaded classes? When asked to load a class, a compliant class loader should first delegate the request to the parent class loader. Only if the parent fails to load the class, it attempts to define and then load the class itself.

The third column shows the total number of objects loaded by that class loader. That number becomes interesting when looking at leaking class loaders (see below).

Expand a class loader to see the parent class loader and the defined classes:

Class Loader Explorer (expanded)

In this particular case, the class loader org.eclipse.ui.workbench has defined the class …HandlerActivation of which 1907 instances are alive.

Listing Duplicate Classes

The easiest way to save perm space is too avoid loading the same class multiple times. Common sense you say? But it is so easy to do: just package the libraries of the Java Architecture for XML Binding (JAXB) into your WAR or plug-in bundle. Starting with JDK 6, those classes are already contained in the JDK classes. Admittedly, this is a special case. For example, some projects just choose to package the same libraries into multiple WARs/plug-ins.

This is how you can list the duplicate classes in the Memory Analyzer:

Pick the Duplicate Classes

What is a Leaking Class Loader?

A leaking class loader is just like any other leak: an object which is still around even though it is not used by the application anymore. Because a class loader keeps references to all classes it defined, this is particularly wasteful in terms of perm space. If you then have a system with regular deploy-undeploy-deploy cycles like an application server, these leaks will quickly eat up the available perm space. And increasing the perm space is no option here…

The above mentioned article uses the following example:

Code Leak Servlet

The servlet LeakServlet is logging something using the Java logging facilities and then writes a Hello World! into the output stream. Please note the anonymous inner class assigned to theCUSTOMLEVEL attribute (Level’s constructor is protected).

I deployed this servlet inside a WAR and then re-deployed it again. Then I took a heap dump (see my previous blog or Memory Analyzer’s WIKI).

Running the Class Loader Overview, I get this result:

Class Loader Explorer (Leak)

As you can see, there are two instances of the /leak class loader (again, so much easier if the class loader name is resolved). Usually, the instance with less number of instances is the troublemaker. Why? When the application is undeployed, normally most of the objects are garbage collected.

To understand why the class loader is still alive, we call Path to GC Root on the class loader object:

Pick Path to GC Roots

The Memory Analyzer now does a breadth first search for Garbage Collection (GC) Roots. GC roots are objects that stay around by definition. For example currently running threads cannot be removed even if no other objects holds a reference to them (they run!). Or parameters of methods currently executed. Or classes loaded by the boot class loader. The reference chain from the leaking object to the GC root is essential in understanding why the /leak class loader has not been garbage collected.

This is the result:

Path to GC Roots

The Memory Analyzer found 3 paths:

  • The class loader is still alive, because a class is still alive (LeakServlet$1). The class is still alive, because an instance is still alive. And that instance is reference through an ArrayListfrom the static field known of the Level class.

What happened here? The Level class keeps a static reference to all known levels. As it is a system class, it will only be removed when the VM is terminated. Alas, our custom level and with it the class loader and all defined classes are only removed when the VM terminates.

The following graph depicts the objects graph found in the dump:

Leak (Object Graph)

Not much you can do about this particular case. But then again it is uncommon to define custom level. However, the method to locate such leaks remains the same.

The other two paths I found also interesting:

  • On first usage, the LogManager registers a shutdown hook (seeRuntime#addShutdownHook). The hook is a thread which is started whenever the VM exits normally. This thread inherits our /leak class loader as context class loader just because we happen to be the first to use the log manager…
  • The third path involves a TimerThread which (if I understood it right) is used to time out the sessions in the server. If I had waited longer than the session timeout until taking the heap dump, this path would not be there.

Of course, running Java Basics -> Duplicate Classes digs up the same class loaders:

Duplicate Classes

What about Interned Strings?

As I mentioned before, the perm space contains - besides class and method data - also interned Strings. Those are Strings whose intern() method has been called and which are therefore pooled. This is usually done for performance reasons: to compare Strings, one can then use the equals operator == instead of called A.equals(B).

To make a long store short, unfortunately the heap dump contains no information about interned Strings.

Conclusion

Wao, this post got longer than expected! As you can see, the heap dump can give you after all some indication concerning perm space. You cannot do much about too many classes (increase the perm space or uninstall plug-ins) and you cannot do much about interned Strings (no info there). But what you can do is make sure you do not have leaking class loaders on board. And that is where the Memory Analyzer can help.

Comments