Update: Andrew Wang’s blog post (http://www.umbrant.com/blog/2012/twitter_jvm_tuning.html) is a pretty nice quick guide, well worth reading in conjunction with this page.
Sorting out sensible JVM memory settings in a production environment can be a tricky business. There are some great articles, blogs and lengthy white-papers out there explaining the details of how the JVM manages memory, but I’ve yet to come across a simple guide that actually helps configure the key JVM startup parameters.
For the impatient: Right down at the bottom of this page I have included a simple tool for helping to set your JVM parameters.
For those who are new to this, I suggest doing some web searches around “java heap”. Other people have done a much better job of explaining the theory behind this.
Whilst the theory is well worth reading, I’m hoping this provides some more practically useful information for people running production Java applications. In this context, I’m dealing with a Tomcat 7 server running on the Oracle JVM with an IO intensive application that performs a lot of in-memory caching.
First up, fire up VisualVM (included with your JDK in the /bin folder) if you haven’t already. This will allow you to interrogate the memory of your running application. If you’re trying to interrogate a remote system, you’ll want to run jstatd in order to get a connection. There’s information elsewhere on the web about how to do this; I’ll probably summarise it in another post later.
That will give you a beautiful screen like this:
The server in this example has a 6GB heap allocation and 150MB PermGen allocation. The applicable JVM startup parameters are:
-server -Xms6g -Xmx6g -XX:MaxPermSize=150m -XX:PermSize=150m -XX:NewRatio=2 -XX:SurvivorRatio=10
To run through these quickly; ‘server’ tells the JVM to run in server mode. This mainly changes the behaviour of the HotSpot compiler. In general things will take a little longer to ‘warm up’, but long-running performance will be improved. There are other implications too, which are well documented elsewhere.
Setting the Xmx and Xms values to be the same, in this instance 6GB, we create a single (hopefully un-fragmented) heap allocation for the virtual machine. It’s this 6GB that then get’s carved up into the areas shown in the screenshot above.
The PermGen space is assigned with MaxPermSize and PermSize. In general you don’t need to allocate much here; this mostly relates to the number of classes that you have loaded; relatively simple applications without loads of library inclusions get away with very small values here. We’re using 150MB in this example, which is more than enough. Again, setting the values the same saves the JVM scaling up and down with usage.
Now for the interesting stuff. The heap is split into Eden Space, Survivor Space and Old Generation. Without going into excruciating detail, objects are created in the Eden space and then moved to the Survivor Space and finally Old Generation. The idea is that the majority of the objects that you create only need to last for a very short period of time (consider an Iterator for example; it only needs to exist for the duration of a single loop). By creating objects in the Eden space, the initial garbage collections on this space are very efficient, since they potentially scrub very large percentages of all objects. Anything which is still in use is promoted to one of the survivor spaces, and then eventually to the Old Generation.
Performing garbage collection on the Old Generation is usually more time consuming, so that’s what you ideally want to avoid when scaling the other spaces.
It’s worth noting that the Survivor Space is actually split into two parts, known as S0 and S1. Only one of these is active at a time, and it receives the objects being tenured from the Eden space.
The JVM settings that you use to define these spaces are the ‘NewRatio’ and the ‘SurvivorRatio’.
The NewRatio is the ratio between the Old Generation and the sum of Survivor Space (both S0 and S1) and Eden Space (in this context, ‘New’ means Survivor and Eden). For example, a NewRatio of 2 will create an Old Generation that is twice the size of Eden plus Survivor (or 2/3 of the heap).
The SurvivorRatio is the ratio of one of the Survivor Spaces and the Eden Space. For example, a Survivor Ratio of 10 will mean that the Eden space is 10 times the size of each of S0 and S1.
So clearly it’s not a simple task to visualise how the heap will be allocated when you’re setting these values.
I have already put in values from my example above; that is a heap of 6GB with a NewRatio of 2 and a SurvivorRatio of 10. This is actually an unusually high survivor ratio, however having watched our application in production for a few weeks we’ve been able to tune it to our [rather unusual] workload.
|Xmx / Xms||MB|
|Survivor Spaces|| MB
Please leave a comment if you have any questions – I’d be really interested to hear if this helps to solve your problems.