Java VM Options You Should Always Use in Production

This post is a cheatsheet with enumeration of options, which should be always used to configure Java Virtual Machine for Web-oriented server applications (i.e. Web Front-End) in production or production-like environments.

For lazy readers full listing is here (for curious detailed explanation is provided below):

  • Java < 8
    -server
    -Xms<heap size>[g|m|k] -Xmx<heap size>[g|m|k]
    -XX:PermSize=<perm gen size>[g|m|k] -XX:MaxPermSize=<perm gen size>[g|m|k]
    -Xmn<young size>[g|m|k]
    -XX:SurvivorRatio=<ratio>
    -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled
    -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=<percent>
    -XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark
    -XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Xloggc:"<path to log>"
    -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M
    -Dsun.net.inetaddr.ttl=<TTL in seconds>
    -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<path to dump>`date`.hprof
    -Djava.rmi.server.hostname=<external IP>
    -Dcom.sun.management.jmxremote.port=<port> 
    -Dcom.sun.management.jmxremote.authenticate=false 
    -Dcom.sun.management.jmxremote.ssl=false
  • Java >= 8
    -server
    -Xms<heap size>[g|m|k] -Xmx<heap size>[g|m|k]
    -XX:MaxMetaspaceSize=<metaspace size>[g|m|k]
    -Xmn<young size>[g|m|k]
    -XX:SurvivorRatio=<ratio>
    -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled
    -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=<percent>
    -XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark
    -XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Xloggc:"<path to log>"
    -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M
    -Dsun.net.inetaddr.ttl=<TTL in seconds>
    -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<path to dump>`date`.hprof
    -Djava.rmi.server.hostname=<external IP>
    -Dcom.sun.management.jmxremote.port=<port> 
    -Dcom.sun.management.jmxremote.authenticate=false 
    -Dcom.sun.management.jmxremote.ssl=false

Make Server a Server

-server

Turns Java VM features specific to server applications, such as sofisticated JIT compiler. Though this option is implicitely enabled for x64 virtual machines, it still makes sence to use it as according to documentation behaviour maybe changed in the future.

Make your Heap Explicit

-Xms<heap size>[g|m|k] -Xmx<heap size>[g|m|k]

To avoid dynamic heap resizing and lags, which could be caused by this, we explicitely specify minimal and maximal heap size. Thus Java VM will spend time only once to commit on all the heap.

-XX:PermSize=<perm gen size>[g|m|k] -XX:MaxPermSize=<perm gen size>[g|m|k]

Logic for permanent generation is the same as for overall heap β€” predefine sizing to avoid costs of dynamic changes. Not applicable to Java >= 8.

-XX:MaxMetaspaceSize=<metaspace size>[g|m|k]

By default Metaspace in Java VM 8 is not limited, though for the sake of system stability it makes sense to limit it with some finite value.

-Xmn<young size>[g|m|k]

Explicitely define size of the young generation.

-XX:SurvivorRatio=<ratio>

Ratio which determines size of the survivour space relatively to eden size. Ratio can be calculated using following formula:

Learn more and this one too.

Make GC Right

-XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled

As response time is critical for server application concurrent collector feets best for Web applications. Unfortunatelly G1 is still not production ready, thus we have to use Concurrent Mark-Sweep collector.

-XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=<percent>

By default CMS GC uses set of heuristic rules to trigger garbage collection. This makes GC less predictable and usually tends to delay collection until old generation is almost occupied. Initiating it in advance allows to complete collection before old generation is full and thus avoid Full GC (i.e. stop-the-world pause). -XX:+UseCMSInitiatingOccupancyOnly prevent usage of GC heuristics. -XX:CMSInitiatingOccupancyFraction informs Java VM when CMS should be triggered. Basically, it allows to create a buffer in heap, which can be filled with data, while CMS is working. Thus percent should be back calculated from the speed in which memory is consumed in old generation during production load. Such percent should be chosen carefully, if it will be small β€” CMS will work to often, if it will be to big β€” CMS will be triggered too late and concurrent mode failure may occur. Usually -XX:CMSInitiatingOccupancyFraction should be at the level 70, which mean that application should utilize less that 70% of old generation.

-XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark

Instructs garbage collector to collect young generation before doing Full GC or CMS remark phase and as a result improvde their performance due to absence of need to check references between young generation and tenured.

Learn more and something fresh.

GC Logging

-XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Xloggc:"<path to log>"

These options make Java to log garbage collector activities into specified file. All records will be prepended with human readable date and time. Meanwhile you should avoid using -XX:+PrintGCTimeStamps as it will prepend record with useless timestamp since Java application start.

Generate log can be later analysed with GCViewer.

-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M

GC log files rotation makes analysis of garbage collection problems easier, also it guaranties that disk is protected from space overconsumption.

Learn more and a little bit more.

DNS Caching

-Dsun.net.inetaddr.ttl=<TTL in seconds>

Number of seconds during which DNS record will be cached in Java VM. Default behaviour of Java VM 6 was to cache forever, which does not feet server application needs, as you would not want to restart server each time, when IP has changed in DNS record. It has been changed in Java VM 7 to cache for 30 seconds, but only if security manager is not installed. Depending on version of Java VM and presence of security manager DNS records still can be cached infinitely.
Commonly recommended option is to disable DNS caching at all, which can be reason for performance degradation. Requests to DNS are performed in synchorized block and only one request is performed in any point in time. Thus, if your application is havily utilizing network it will experience saturation on DNS requests, so TTL value 0 should never be used.

Better solution is to cache for reasonable not long period, for example 1 minute (60 seconds). This mean that system your application is interacting with have to guaranty that two different IPs will continue to work properly during this 1 minute, otherwise lower TTL value should be chosen. It should always be reasonable (not equal to 0) to prevent possible contention of requests to DNS.

This option is convinient to use, but networkaddress.cache.ttl specified in %JRE%/lib/security/java.security should be considered as better solution, at least from official documentation prospective.

Learn more.

Dump on Out of Memory

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<path to dump>`date`.hprof

If your application would ever fail with out-of-memory in production, you would not want to wait for another chance to reproduce the problem. These options instruct Java VM to dump memory into file, when OOM occurred. It can cause considerable pauses on big heaps during OOM event. However, if Java VM is at OOM already, collecting as much information about the issue is more important than trying to serve traffic with completely broken application. We are also making sure the dump would be created with name unique per application start time (this is requeired because Java VM will fail to overwrite existing file).

Learn more.

Make JMX work

-Djava.rmi.server.hostname=<external IP>

IP address, which would be embedded into RMI stubs sent to clients. Later clients will use this stubs to communicate with server via RMI. As in modern datacenters machine often has two IPs (internal and external) you would want explicitly specify which IP to use, otherwise JVM will make it’s own choice. Correct value of this property is a precondition for successfully using JMX.

-Dcom.sun.management.jmxremote.port=<port> 
-Dcom.sun.management.jmxremote.authenticate=false 
-Dcom.sun.management.jmxremote.ssl=false

-Dcom.sun.management.jmxremote option is not required starting from Java VM 6, however to make JMX available for remote access specific listen port should be provided. Later this port can be used to connect using any JMX tool (e.g. VisualVM or Java Mission Control).

Also to reduce additional troubles with connecting disable standard authentication, but be sure that only authorized users can connect to environment using firewall. If for security reasons you still want to control access via standard mechanism learn more.

P.S.

Many options that were previously very useful with updates in Java VM (specifically release of Java 7) are preconfigured by default:

  • -XX:+UseCompressedOops
  • -Dsun.rmi.dgc.server.gcInterval=3600000 and -Dsun.rmi.dgc.client.gcInterval=3600000
  • -Dcom.sun.management.jmxremote