Skip to content

Instantly share code, notes, and snippets.

@erikdw
Forked from changreytang/MetaspaceResearch.md
Created September 8, 2017 18:54
Show Gist options
  • Save erikdw/e730a1249887bb4462f226658351cfa4 to your computer and use it in GitHub Desktop.
Save erikdw/e730a1249887bb4462f226658351cfa4 to your computer and use it in GitHub Desktop.
Research the impacts of the replacement of PermGen with Metaspace from JDK7 to JDK8

Metaspace Research

What is PermGen?

  • The Permanent Generation memory pool contains permanent class metadata and descriptors information when classes are loaded
    • PermGen space is always reserved for classes and items that are attached to them (i.e., static members)
  • PermGem space is contiguous in memory to the Java heap and have the same rounds of Garbage Collection but it is not part of the Java heap

What is Metaspace?

  • In JDK8, the PermGen space has been entirely replaced by Metaspace which is no longer contiguous to the Java heap and now exists in native memory
  • The maximum size of Metaspace relies on the amount of native memory space available because, by default, it is dynamically resizeable based on need (the more classes you load, the more memory it will allocate)
    • IMPORTANT: The removal of PermGen doesn't mean that your class loader leak issues are gone. It just now means that a leak will end up consuming your entire native memory causing swapping that would only get worse!

Metaspace Memory Management

  • Instead of having GC tied to the Java heap, the Metaspace has a Metaspace VM which employs its own memory management techniques
  • One rule is that as long as the classloader is alive, the metadata remains alive in the Metaspace and cannot be freed
  • Per classloader storage area is called "a metaspace", and these metaspaces come together to collectively form the Metaspace
    • The reclamation of the metaspace per classloader can only happen after its classloader is no longer alive and is reported dead by the GC
  • How the dynamic resizing works is that there is a initial high-water-mark (configurable with the -XX:MetaspaceSize config) that denotes when to resize
    • Once the watermark is hit, a full GC is triggered to unload the classes if their classloaders are no longer alive and the new value of the high-water-mark is reset depending on the amount of freed Metaspace
    • If insufficient space is freed, the high-water-mark goes up; if too much space is freed, the high-water-mark goes down
    • IMPORTANT: Full GC is expensive so it is advisable to set the -XX:MetaspaceSize to a higher value in order to avoid initial garbage collections

Metaspace Configurations

  • XX:MetaspaceSize={ } where { } is the initial amount of space (the initial high-water-mark) allocated for class metadata (in bytes) that may induce a GC to unload classes. The amount is approximate. After the high-water-mark is first reached, the next high-water-mark is managed by the garbage collector
    • By default set to 16M on 32bit and 21M on 64bit
  • XX:MaxMetaspaceSize={ } where { ] is the maximum amount of space to be allocated for class metadata (in bytes). This flag can be used to limit the amount of space allocated for class metadata. This value is approximate.
    • By default there is no limit set, meaning that is possible for Metaspace to grow to the size of native memory available
  • XX:MinMetaspaceFreeRatio={ } where { } is the minimum percentage of class metadata capacity free after a GC to avoid an increase in the amount of space (high-water-mark) allocated for class metadata that will induce a garbage collection.
    • Increasing will make the Metaspace grow more aggressively
  • XX:MaxMetaspaceFreeRatio={ ] where { } is the maximum percentage of class metadata capacity free after a GC to avoid a reduction in the amount of space (high-water-mark) allocated for class metadata that will induce a garbage collection.
    • Increasing will reduce the chance of Metaspace shrinking

Metaspace Cautions

  • Allowing Metaspace to dynamically resize without a cap can cause it to consume your entire native memory causing swapping if there is a class loader leak
  • The dynamic resizing is expensive because it triggers a full GC to unload classes everytime the high-water-mark is reached
  • The Metaspace VM employs a chunking allocator with multiple chunk sizes depending on the type of classloader, there are chances that free chunks may not be of the same size as the chunk needed for a class item which leads to fragmentation
    • The Metaspace VM doesn’t yet employ compaction so fragmentation is a major concern at this moment
  • Even if XX:MaxMetaspaceSize is set, the native memory footprint reserved is twice as much as the maximum size specified
    • This means that if you capped the Metaspace size at 128MB it would still reserve around 256MB of native memory

Debugging Metaspace

  • NOTE: With JDK8 build 13, you must start Java with -XX:+UnlockDiagnosticVMOptions in order for some of the following to work
  • jmap –clstats {PID} prints class loader statistics.
    • This now supersedes –permstat that used to print class loader stats for JVMs prior to JDK8
  • jstat –gc {LVMID} now prints Metaspace information
  • jcmd {PID} GC.class_stats 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.

Conclusion and Suggestions

  • To combat the issues brought up in the Metaspace Cautions portion of this document, I suggest that we reduce or even entirely remove the ability for dynamic resizing to the Metaspace and also cap the maximum size. This basically allows Metaspace to effectively behave just like the PermGen space but now existing in native memory. The following configuration will achieve this:
-XX:MetaspaceSize=<initial_size> 
-XX:MaxMetaspaceSize=<initial_size> 
-XX:MinMetaspaceFreeRatio=0 
-XX:MaxMetaspaceFreeRatio=100

Sources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment