Garbage collection is the process by which Java’s runtime system identifies and removes unreferenced objects in memory. It plays a critical role in managing memory by clearing up unreferenced objects, temporary objects, unused metadata, and more.
In this tutorial, we’ll explore Metaspace and the Metadata GC Threshold, along with how to tune their parameters.
Metadata contains information about the objects in the heap, including their class definitions, method tables, and related information. Depending on the version of Java, the JVM stores this data in the permanent generation or Metaspace.
The JVM relies on this information to perform tasks like class loading, bytecode verification, and dynamic binding.
Starting with Java 8, the JVM replaced the permanent generation or PermGen with a new area in memory called the Metaspace to store metadata.
The PermGen was a fixed-size memory area separate from the main heap used to store metadata for the classes and methods loaded by the JVM. It had a fixed maximum size, and once it was full, the JVM would throw an OutOfMemoryError.
The Metaspace is not fixed in size and can grow dynamically, depending on the amount of metadata in the application. The Metaspace is present separately from the main heap in a segment of native memory (process memory). It is only limited in size by the constraints set by the host operating system.
In Java, when we create an object with metadata, it occupies memory space just like any other object. The JVM requires this metadata for various tasks. Unlike regular objects, metadata does not undergo garbage collection until a certain threshold is reached.
As more classes get loaded dynamically during the execution of a Java program, the Metaspace fills up.
The JVM maintains a threshold for the size of the Metaspace contents, and when a particular allocation doesn’t fit within this threshold, it triggers a Metadata GC Threshold garbage collection cycle.
We can use JVM flags like -XX:+PrintClassHistogram and -XX:+PrintMetaspaceStatistics to gather information about classes that use a lot of metadata memory and print statistics about the Metaspace, such as the amount of space used for class metadata.
Using this information, we can aim at optimizing our code to improve the usage of Metaspace and Garbage Collection cycles. Additionally, we can also tune the JVM parameters associated with Metaspace.
Let’s take a look at some of the JVM parameters used to tune metadata and Metaspace.
This parameter helps to set the initial amount of space (initial high-water-mark) allocated for class metadata (in bytes) that may induce a Garbage Collection to unload classes. The amount is approximate, which means the JVM can decide to increase or decrease the size of the Metaspace as well.
A large value for this parameter ensures that garbage collection happens less frequently. The default value for the parameter is platform-dependent and ranges from 12 MB to about 20 MB.
For example, we could set it to 128 megabytes:
This parameter sets the maximum size of the Metaspace, after which an OutOfMemory error is thrown. This flag limits the amount of space allocated for class metadata, and the value assigned to this parameter is an approximate one.
By default, there’s no limit set, meaning that it’s possible for the Metaspace to grow to the size of the native memory available.
As an example, let’s set it to 100 megabytes:
This feature aims to reduce the memory footprint of 64-bit Java applications by compressing object references. When this parameter is set to true, the JVM will use compressed pointers for class metadata, which allows the JVM to use 32-bit addressing for class-related data instead of the full 64-bit pointers.
This optimization becomes especially valuable on 64-bit architectures because it reduces the memory usage of class metadata, as well as the memory required for object references, thus potentially improving the application’s overall performance.
Compressed class pointers keep class space allocations separate from non-class space allocations. So, we have two global Metaspace contexts: one holding allocations of Klass structures (compressed class space), and one holding everything else (non-class Metaspace).
It’s important to note that in recent versions of the JVM, the flag is typically enabled by default when running a 64-bit Java application, so we don’t usually need to set this flag explicitly.
This JVM flag enables or disables the use of compressed pointers for Java objects in 64-bit JVMs. When the parameter is set to true, the JVM will use compressed pointers, which means that object references will use 32-bit pointers instead of the full 64-bit pointers.
With compressed pointers, it is only possible to address a smaller range of memory. This forces the JVM to use smaller pointers and save memory.
In this article, we’ve explored the concept of metadata and Metaspace.
We explored the reason behind triggering of Metadata GC Threshold. Also, we learned about the Metadata GC Threshold and the different JVM parameters available to tune Metaspace and optimize the garbage collection cycles.