Java 8 and the memory model

In my previous post (which you can find here ), I went over the java 7 memory model and explained the major parts. The memory model for java 8 looks very much the same. There is one major difference however.

R.I.P PermGen

The permanent generation is no more. It is gone, removed, killed. The direct result of this is that starting from java 8, there will be no more OutOfMemoryError: PermGen space errors, ever again. That is something to be happy about. That particular error caused a lot of misunderstandings and an ocean of time wasted while trying to find the exact cause.

The reason for removing the PermGen space is because it is difficult to manage and tune. The maximum size of the permanent generation was configured before starting up your java application. You can not change it’s maximum size after the JVM was started. This is annoying because the PermGen space contained all of the metadata about your loaded classes. So how many classes get loaded during the lifetime of your application?  This is already a major factor on the size of the Permanent Generation, but there were other factors as well, such as the constant pool, the size of the methods in the those classes, etc etc. Also garbage collecting the PermGen space required some specialised code (according to this article) and was considered as something that should be more simple.

The Metaspace

The JVM still needs a place to hold the metadata about the loaded classes. It is called the Metaspace (its a better name then permanent generation in my opinion) and it is the replacement of the permanent generation. Its reason for existing is almost exactly the same. The metaspace also stores all kinds of metadata about classes that have been loaded.

What are the main differences then? (An ‘official’ note on the differences can be found here)

  • Well, first of all, the metaspace uses native memory and not ‘java memory’.
  • By default the size of the metaspace is only limited by the amount of native memory available on the machine where the JVM runs on.
  • The metaspace is managed by something called the Metaspace VM. This VM can decide to either shrink or expand the metaspace. I’ll discuss how this works a bit further down this post.
  • The Klass structures used as a tool to describe classes (the actual model for class metadata) have been removed.
  • The  PermGen environment parameters -XX:PermSize and -XX:MaxPermSize are ignored when they are found on the command line.
  • Garbage collections will occur when the metaspace is about full.

The native memory pitfall

While everybody surely danced because of the fact that there will never be an OOM caused by that PermGen space ever again, clever readers already figured out that the default behaviour of the metaspace could can actually cause bigger trouble then just the JVM that runs out of memory. By default, the metaspace has the potential to ‘devour’ all of the available native memory. This would not only cause trouble for your JVM, it causes trouble for other processes running on that machine as well.

This can be avoided by adding the following environment parameters to the command line: -XX:MaxMetaspaceSize. This will limit the growth of the metaspace to the amount assigned to the parameter.

How the sizing of the metaspace works

I go this information from an article which you can find here. Basically, the Metaspace VM uses something called ‘a high watermark’. Look at it as a threshold. When the actual size of the metaspace reaches this high watermark, a full garbage collection is triggered, trying to unload classes and classloaders that have died from the metaspace.  The high watermark, this threshold, is being reset then. The new value of the high watermark depends on how much memory has been freed from the garbage collection.

If there is not enough memory freed from the last garbage collection, the new high watermark will get a higher value. If there is too much memory freed, the value of the high watermark will be lower. When you start a java application, it is possible that the initial high watermark is just too low. This might trigger a few grow cycles with full garbage collections especially when the new high watermark is not high enough yet. This can be avoided using an environment parameter: –XX:MetaspaceSize. This sets the initial high watermark to a value of your choice. Do note that if the given value is still way too low, you will end up with a grow cycle anyway.