The Java Virtual Machine (JVM) provides two major services for any Java application: memory management, and running the compiled byte codes. Much of the memory management is done by the garbage collector, and the compiled byte codes are run by the HotSpot compiler.
Garbage Collection Revision
The garbage collector plays a crucial role in managing heap memory. It tracks every object in the heap, and removes any unreferenced objects. The garbage collector also removes temporary objects and unused class metadata.
Java uses generational garbage collection. The more garbage collection events an object survives, the further it gets promoted into older generations. It starts in the young generation (which is divided into Eden and Survivor spaces), and eventually ends up in the tenured (old) generation if it survives long enough. See the previous post on garbage collection
JVM Memory
The JVM memory is divided into two main groups: heap and non-heap.
All Java programmers probably know about the heap. The heap stores the objects that are created while a Java application runs. After a new instance of an object is created, it lives in the heap until it is no longer referenced. Then the garbage collector can free the memory allocated to that object. This implies that the heap size can change dynamically as the application runs.
The JVM initialises the heap at start-up. We can configure some heap size settings using the -XX:MaxHeapSize
(or -Xmx
), -XX:InitalHeapSize
(or -Xms
), -XX:NewSize
(or -Xmn
) and -XX:MaxNewSize
JVM options.
Non-heap memory makes up the rest of JVM memory. Non-heap memory is divided into several different areas, each storing different data. This includes JVM code and internal structures, the constant pool, interned strings, field and method metadata, and the code for methods and constructors.
PermGen
An important part of the non-heap memory is allocated to PermGen (the permanent generation). This is a pool containing reflective data of the virtual machine itself, such as class and method objects. PermGen contains metadata required by the JVM to describe the classes and methods used in the application. It is populated at runtime with these application classes, as well as Java SE library classes and interned strings.
Even though PermGen is not part of the main heap, it is included in a full garbage collection. Classes may get unloaded (garbage collected) if they are no longer needed and space is required for other classes.
The maximum size of PermGen is fixed using the -XX:MaxPermSize=size
option. There is no way to make PermGen auto-increase past this allocated size. Because of this fixed size, PermGen is difficult to tune.
Metaspace
From Java 8 PermGen was replaced by a new memory area called Metaspace. It has the same role as PermGen which is to store class metadata. Metaspace is not part of the JVM heap, but is allocated in native operating system memory. It can auto-increase as needed to load additional class metadata.
When PermGen was originally introduced, there was no dynamic class unloading. Once a class was loaded, it was stuck in memory permanently until the JVM was shut down (hence the “PermGen” name). These days classes can be loaded and unloaded during the lifespan of the JVM. “Metaspace” is better suited as a name for the area where the metadata is kept.
Metaspace can auto-increase its size up to what the operating system can provide. We can set a fixed maximum size for Metaspace using the -XX:MaxMetaspaceSize
flag. The default maximum size is unlimited, which means that the operating system memory is the only limit.
Garbage collection of the dead classes and class loaders is triggered once the class metadata usage reaches the -XX:MetaspaceSize
limit.
With the removal of PermGen, we will never get another OutOfMemoryError: PermGen error
. Obviously the -XX:PermSize
and -XX:MaxPermSize
JVM options are now ignored.
ClassLoader Leaks
Both PermGen and Metaspace can suffer from ClassLoader
leaks. The removal of PermGen doesn’t mean that our class loader leak issues are gone. We still have to monitor memory usage and plan accordingly.
The default JVM settings for Metaspace allow it to automatically increase its size as needed. It will obviously then take longer before the effects of any ClassLoader
leaks are seen. This just pushes the problem further away without necessarily solving it. A leak could end up consuming our entire native memory. The effect of running out of OS memory will probably be more severe than running out of JVM PermGen memory.
Since the class metadata is allocated from native operating system memory, the maximum available space is the total available system memory. Thus, we will no longer get OutOfMemoryError
s, but could end up spilling into the operating system swap space.
Further Reading
For more technical details on the removal of the permanent generation, see JEPS 122.
For new developments on Metaspace, see JEP 387: Elastic Metaspace.
There’s a good PermGen vs Metaspace article on DZone. As well as a nice short one on Baeldung.
There are two nicely illustrated technical articles on the Java-Latte blogspot, one on garbage collection, the other on Metaspace. They’re about 10 years old, but still relevant.
Was this interesting? Please share your comments, and as always, stay safe and keep learning!