InfoQ Homepage Articles Where Has the Java PermGen Gone?

# Where Has the Java PermGen Gone?

Leia em Português

Lire ce contenu en français

The Java Virtual Machine (JVM) uses an internal representation of its classes containing per-class metadata such as class hierarchy information, method data and information (such as bytecodes, stack and variable sizes), the runtime constant pool and resolved symbolic reference and Vtables.

In the past (when custom class loaders weren’t that common), the classes were mostly “static” and were infrequently unloaded or collected, and hence were labeled “permanent”. Also, since the classes are a part of the JVM implementation and not created by the application they are considered “non-heap” memory.

For HotSpot JVM prior to JDK8, these “permanent” representations would live in an area called the “permanent generation”. This permanent generation was contiguous with the Java heap and was limited to -XX:MaxPermSize that had to be set on the command line before starting the JVM or would default to 64M (85M for 64bit scaled pointers). The collection of the permanent generation would be tied to the collection of the old generation, so whenever either gets full, both the permanent generation and the old generation would be collected. One of the obvious problems that you may be able to call out right away is the dependency on the ‑XX:MaxPermSize. If the classes metadata size is beyond the bounds of ‑XX:MaxPermSize, your application will run out of memory and you will encounter an OOM (Out of Memory) error.

Trivia: Before JDK7, for HotSpot JVM, interned-strings were also held in the permanent generation aka PermGen, causing a vast number of performance issues and OOM errors. For more information on the removal of interned strings from the PermGen please see here

## Bye, Bye PermGen, Hello Metaspace!

With the advent of JDK8, we no longer have the PermGen. No, the metadata information is not gone, just that the space where it was held is no longer contiguous to the Java heap. The metadata has now moved to native memory to an area known as the “Metaspace”.

The move to Metaspace was necessary since the PermGen was really hard to tune. There was a possibility that the metadata could move with every full garbage collection. Also, it was difficult to size the PermGen since the size depended on a lot of factors such as the total number of classes, the size of the constant pools, size of methods, etc.

Additionally, each garbage collector in HotSpot needed specialized code for dealing with metadata in the PermGen. Detaching metadata from PermGen not only allows the seamless management of Metaspace, but also allows for improvements such as simplification of full garbage collections and future concurrent de-allocation of class metadata.

## What Does The Removal Of Permanent Space Mean To The End Users?

Since the class metadata is allocated out of native memory, the max available space is the total available system memory. Thus, you will no longer encounter OOM errors and could end up spilling into the swap space. The end user can either choose to cap the max available native space for class metadata, or the user can let the JVM grow the native memory in-order to accommodate the class metadata.

Note: The removal of PermGen doesn’t mean that your class loader leak issues are gone. So, yes, you will still have to monitor your consumption and plan accordingly, since a leak would end up consuming your entire native memory causing swapping that would only get worse.

## Moving On To Metaspace And Its Allocation:

The Metaspace VM now employs memory management techniques to manage Metaspace. Thus moving the work from the different garbage collectors to just the one Metaspace VM that performs all of its work in C++ in the Metaspace. A theme behind the Metaspace is simply that the lifetime of classes and their metadata matches the lifetime of the class loaders’. That is, as long as the classloader is alive, the metadata remains alive in the Metaspace and can’t be freed.

We have been using the term “Metaspace” loosely. More formally, per classloader storage area is called “a metaspace”. And these metaspaces are collectively called “the Metaspace”. The reclamation of metaspace per classloader can happen only after its classloader is no longer alive and is reported dead by the garbage collector. There is no relocation or compaction in these metaspaces. But metadata is scanned for Java references.

The Metaspace VM manages the Metaspace allocation by employing a chunking allocator. The chunking size depends on the type of the classloader. There is a global free list of chunks. Whenever a classloader needs a chunk, it gets it out of this global list and maintains its own chunk list. When any classloader dies, its chunks are freed, and returned back to the global free list. The chunks are further divided into blocks and each block holds a unit of metadata. The allocation of blocks from chunks is linear (pointer bump). The chunks are allocated out of memory mapped (mmapped) spaces. There is a linked list of such global virtual mmapped spaces and whenever any virtual space is emptied, its returned back to the operating system.

The figure above shows Metaspace allocation with metachunks in mmapped virtual spaces. Classloaders 1 and 3 depict reflection or anonymous classloaders and they employ “specialized” chunk size. Classloaders 2 and 4 can employ small or medium chunk size based on the number of item in those loaders.

## Tuning And Tools For Metaspace:

As previously mentioned, a Metaspace VM will manage the growth of the Metaspace. But there may be scenarios where you may want to limit the growth by explicitly setting the -XX:MaxMetaspaceSize on the command line. By default, the –XX:MaxMetaspaceSize doesn’t have a limit, so technically the Metaspace size could grow into swap space and you would start getting native allocation failures.

For a 64-bit server class JVM, the default/initial value of –XX:MetaspaceSize is 21MB. This is the initial high watermark. Once this watermark is hit, a full garbage collection is triggered to unload classes (when their classloaders are no longer alive) and the high watermark is reset. The new value of the high watermark depends on the amount of freed Metaspace. If insufficient space is freed up, the high watermark goes up; if too much space is freed, the high watermark goes down. This will be repeated multiple times if the initial watermark is too low. And you will be able to visualize the repeated full garbage collections in your garbage collector logs. In such a scenario, you are advised to set the –XX:MetaspaceSize to a higher value on the command line in order to avoid the initial garbage collections.

After subsequent collections, the Metaspace VM will automatically adjust your high watermark, so as to push the next Metaspace garbage collection further out.

There are also two options: ‑XX:MinMetaspaceFreeRatio and ‑XX:MaxMetaspaceFreeRatio. These are analogous to GC FreeRatio parameters and they can be set on the command line as well.

A few tools have been modified to help get more information regarding the Metaspace and they are listed here:

• jmap –clstats <PID>: prints class loader statistics. (This now supersedes –permstat that used to print class loader stats for JVMs prior to JDK8). An example output while running DaCapo’s Avrora benchmark:
$jmap -clstats <PID> Attaching to process ID 6476, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.5-b02 finding class loader instances ..done. computing per loader stat ..done. please wait.. computing liveness.liveness analysis may be inaccurate ... class_loader classes bytes parent_loader alive? type <bootstrap> 655 1222734 null live <internal> 0x000000074004a6c0 0 0 0x000000074004a708 dead java/util/ResourceBundle$RBClassLoader@0x00000007c0053e20
0x000000074004a760    0    0      null      dead      sun/misc/Launcher$ExtClassLoader@0x00000007c002d248 0x00000007401189c8 1 1471 0x00000007400752f8 dead sun/reflect/DelegatingClassLoader@0x00000007c0009870 0x000000074004a708 116 316053 0x000000074004a760 dead sun/misc/Launcher$AppClassLoader@0x00000007c0038190
total = 6      1310   2314112           N/A       alive=1, dead=5     N/A    
• jstat –gc <LVMID>: now prints Metaspace information as shown in the following example:

• jcmd <PID> GC.class_stats: This is a new diagnostic command that enables the end user to connect to a live JVM and dump a detailed histogram of Java class metadata.

Note: With JDK8 build 13, you have to start Java with ‑XX:+UnlockDiagnosticVMOptions

$jcmd <PID> help GC.class_stats 9522: GC.class_stats Provide statistics about Java class meta data. Requires -XX:+UnlockDiagnosticVMOptions. Impact: High: Depends on Java heap size and content. Syntax : GC.class_stats [options] [<columns>] Arguments: columns : [optional] Comma-separated list of all the columns to show. If not specified, the following columns are shown: InstBytes,KlassBytes,CpAll,annotations,MethodCount,Bytecodes,MethodAll,ROAll,RWAll,Total (STRING, no default value) Options: (options must be specified using the <key> or <key>=<value> syntax) -all : [optional] Show all columns (BOOLEAN, false) -csv : [optional] Print in CSV (comma-separated values) format for spreadsheets (BOOLEAN, false) -help : [optional] Show meaning of all the columns (BOOLEAN, false) Note: For more information on the columns, please see here. An example output: $ jcmd <PID> GC.class_stats

7140:
Index Super InstBytes KlassBytes annotations   CpAll MethodCount Bytecodes MethodAll   ROAll   RWAll   Total ClassName
1    -1    426416        480           0       0           0         0         0      24     576     600 [C
2    -1    290136        480           0       0           0         0         0      40     576     616 [Lavrora.arch.legacy.LegacyInstr;
3    -1    269840        480           0       0           0         0         0      24     576     600 [B
4    43    137856        648           0   19248         129      4886     25288   16368   30568   46936 java.lang.Class
5    43    136968        624           0    8760          94      4570     33616   12072   32000   44072 java.lang.String
6    43     75872        560           0    1296           7       149      1400     880    2680    3560 java.util.HashMap$Node 7 836 57408 608 0 720 3 69 1480 528 2488 3016 avrora.sim.util.MulticastFSMProbe 8 43 55488 504 0 680 1 31 440 280 1536 1816 avrora.sim.FiniteStateMachine$State
9    -1     53712        480           0       0           0         0         0      24     576     600 [Ljava.lang.Object;
10    -1     49424        480           0       0           0         0         0      24     576     600 [I
11    -1     49248        480           0       0           0         0         0      24     576     600 [Lavrora.sim.platform.ExternalFlash$Page; 12 -1 24400 480 0 0 0 0 0 32 576 608 [Ljava.util.HashMap$Node;
13   394     21408        520           0     600           3        33      1216     432    2080    2512 avrora.sim.AtmelInterpreter$IORegBehavior 14 727 19800 672 0 968 4 71 1240 664 2472 3136 avrora.arch.legacy.LegacyInstr$MOVW
…<snipped>
…<snipped>
1299  1300         0        608           0     256           1         5       152     104    1024    1128 sun.util.resources.LocaleNamesBundle
1300  1098         0        608           0    1744          10       290      1808    1176    3208    4384 sun.util.resources.OpenListResourceBundle
1301  1098         0        616           0    2184          12       395      2200    1480    3800    5280 sun.util.resources.ParallelListResourceBundle
2244312     794288        2024 2260976       12801    561882   3135144 1906688 4684704 6591392 Total
34.0%      12.1%        0.0%   34.3%           -      8.5%     47.6%   28.9%   71.1%  100.0%
Index Super InstBytes KlassBytes annotations   CpAll MethodCount Bytecodes MethodAll   ROAll   RWAll   Total ClassName


## Current Issues:

As mentioned earlier, the Metaspace VM employs a chunking allocator. There are multiple chunk sizes depending on the type of classloader. Also, the class items themselves are not of a fixed size, thus there are chances that free chunks may not be of the same size as the chunk needed for a class item. All this could lead to fragmentation. The Metaspace VM doesn’t (yet) employ compaction hence fragmentation is a major concern at this moment.

Monica Beckwith is a Java Performance Consultant. Her past experiences include working with Oracle/Sun and AMD; optimizing the JVM for server class systems. Monica was voted a Rock Star speaker @JavaOne 2013 and was the performance lead for Garbage First Garbage Collector (G1 GC). You can follow Monica on twitter @mon_beck

Style

## Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

• ##### Java 8 - Metaspace VM

by nithin nambiar /

• ##### Well written article

by Arockiaraj Durairaj /

• ##### No more PermGen Leak

by Rohit Malhotra /

• ##### Re: No more PermGen Leak

by Phani Timmiri /

• ##### Java 8 - Metaspace VM

Your message is awaiting moderation. Thank you for participating in the discussion.

Nice informative article from Monica about Metaspace VM. If I put a request for gc does it kickstart collection in Metaspace as well if it triggers a full GC?

• ##### Well written article

Your message is awaiting moderation. Thank you for participating in the discussion.

The article is well written and very detailed and interesting to read.

• ##### No more PermGen Leak

Your message is awaiting moderation. Thank you for participating in the discussion.

Thank god, there is no more PermGen OutOfMemoryError in Java anymore, I was tired with so many leaks related to our web app in Tomcat.

• ##### Re: No more PermGen Leak

Your message is awaiting moderation. Thank you for participating in the discussion.

That is not right! the problem got moved to else where and it could be worse as your app is now eating into native available memory and if you are sharing the host to run other services, all of those will be impacted now. You should limit the size of meta space now.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Is your profile up-to-date? Please take a moment to review and update.

Note: If updating/changing your email, a validation request will be sent

Company name:
Company role:
Company size:
Country/Zone:
State/Province/Region:
You will be sent an email to validate the new email address. This pop-up will close itself in a few moments.