Nowadays in many cases of the non-functional testing processes using tools, the root cause for non-performing Java applications is the suboptimal configuration for memory usage and garbage collection. The situation is even more complicated since the optimal heap configuration in most cases can neither be calculated nor guessed.
The optimal configuration depends on the size of the objects and their lifespan and those depend on the (type of) application and user behavior. What? Did you just say that the optimal configuration depends on user behavior? Yes, that’s true! Basically what I want to say is that optimizing the heap configuration is not a one-shot operation.
You will not be able to set it right once and forget about it. You should re-evaluate your non functional tool application on a regular basis and check if you have to adapt your JVM configuration to match changed circumstances. On the other hand, the JVM is very forgiving and your application will perform very well with a less than optimal heap configuration.
Every testing company order to optimize the heap configuration for your situation you have to try different settings. Usually, in order to do so, you need some kind of test environment and a load generator which is able to produce realistic load patterns in a repeatable way.
With this setup, you can try different heap configurations and monitor the heap utilization and garbage collector. The best practice is to make an educated guess on the configuration and start optimizing from there. In this article, I will show you how to do this.
Java Memory Management
In order to be able to optimize the heap utilization of your application you need to understand how the Java heap and garbage collector work and how they are configured.
The heap is split up in Young-Generation (Eden-Space, and two Survivor-Spaces of the identical size usually called From and To), Old Generation (Tenured) and Permanent Space.
Heap organization in Eden-, two Survivor-, Tenured-, and Perm-Spaces
The idea behind this organization of the heap is that most of the objects dye in New-Space because they have a very short life cycle:
80-98% of all new instantiated objects dye within the next million of instructions.
80-98% of all new instantiated objects dye before an additional Megabyte of storage is allocated.
To clean up the whole heap within one garbage collector run would take much longer than reducing the cleaning up to just the Young-Generation (Minor-GC). Only the Full-Gc works on the complete heap. Usually, a Minor-Gc is much faster than a Full-Gc.
The execution time of a Gc run is very important to you because most garbage collection strategies stop the world. This means that even if you have multiple processor cores, all execution is halted during the GC run.
The stop-the-world strategies have many negative effects on applications that optimize response times. Today most JVMs also provide an implementation of a mark-and-sweep strategy which eliminates the negative effects because they do not need long pauses.
Heap Configuration Parameters
The next step before you can start optimizing the Java heap and garbage collector configuration is to develop a thorough understanding of the configuration parameters.
defines the minimal/ initial size of the heap
defines the maximum size of the heap
For server applications, you should set both parameters identical because the resizing of the heap also initiates a Full-Gc. With an application under load, this could have a severe impact on application performance from the very start. On most operating systems the regained memory is not returned to the operating system.
initial size of the New Space. This includes Eden plus two Survivor Spaces (From and To).
usually, we set -XX:MaxNewSize the same as -XX:NewSize because resizing would cause a full collection
initial size of the Perm Space
usually, we set -XX:MaxPermSize the same as -XX:PermSize because resizing would cause a full collection
ratio Eden to Survivor-Space; I usually start with a survivor ratio of 8; in this case, each of the survivor spaces (From and To) uses 1/10 of the size of the New-Space
ratio between old and new generation
size of the Permanent Generation
the survivor space can be filled by 90% until the objects are moved to the old generation
number of from-to runs until the object is moved to the old generation
Over time Sun developed different strategies for the garbage collector. Different applications have different requirements regarding response times. In case you optimize for response times or you are having realtime requirements you are in a totally different situation than somebody who is optimizing for throughput.
In this section, I describe the different strategies for you so that you will be able to select the one that fits best to your needs. Different strategies and configuration options are available in the different JVM versions. Check the version of your JVM before you start working.