The Java Virtual Machine (JVM) is the powerhouse behind millions of enterprise applications. As Java developers, tuning and configuring the JVM properly is critical for writing optimized, robust code. The various JVM options give us precise control over memory management, garbage collection, logging and other key behaviors. However, these options are often misunderstood or underutilized.
In this comprehensive guide, we will dive deep into the most important JVM options for building production-ready Java applications. I will share tons of tips from my years of experience as a Java developer to help you master these performance-boosting flags.
Let‘s get started!
A Quick Primer on JVM Options
First, a quick refresher. The JVM options are specified as flags when launching the java process:
java -Xms2G -Xmx4G MyApp
There are three main types of options:
Standard options – Common flags like -Xms and -Xmx that have no prefix.
Non-standard options – Advanced options prefixed with -X.
Advanced options – Experimental flags prefixed with -XX.
To inspect the JVM options used by a running application on Linux:
ps -ef | grep java
This prints the full command line including any configured options.
Alright, now that we‘re caught up, let‘s examine 10 key JVM options every developer should know.
1. Setting Heap Sizes (-Xms and -Xmx)
The most popular tuning options are -Xms and -Xmx which control the initial and maximum Java heap size respectively.
-
-Xmssets the starting size of the JVM‘s memory heap. This avoids resize overhead during initialization. -
-Xmxsets the maximum heap size. The JVM‘s garbage collector kicks in when heap usage hits this limit.
For example:
-Xms2g -Xmx4g
This allocates a 2 GB heap initially, up to a max of 4 GB.
According to GCEdge, the ideal heap size lies between 40 to 80% of total physical memory. Load test your application at scale to finalize optimal -Xms and -Xmx values. A good heap size reduces Garbage Collection (GC) overhead and OutOfMemoryErrors.

Fig 1. Visual representation of the JVM‘s heap memory initialized with -Xms and capped by -Xmx.
2. Setting Percentages for Heap Free Ratio
The heap free ratios directly control when the Garbage Collector runs.
-
-XX:MaxHeapFreeRatio– Maximum % of heap free after GC before it can shrink. -
-XX:MinHeapFreeRatio– Minimum % of heap free after GC before it can expand.
For example:
-XX:MinHeapFreeRatio=40 -XX:MaxHeapFreeRatio=70
Here, the heap won‘t shrink below 40% free space, or expand unless usage drops below 70% free.
According to Plumbr, optimal ratios are application-specific but conservative defaults around 70% and 40% are good.
Monitoring these in JConsole helps fine-tune GC behavior.
3. Enabling Class Data Sharing (-Xshareclasses)
-Xshareclasses enables sharing of class metadata between JVM instances. This reduces startup overhead, footprint and improves response times.
As per Oracle, class data sharing is highly effective in containers and microservices where multiple identical JVMs start up:
"A particular advantage of this is that when starting a Java application, the JVM loads classes and other data into memory. Much of this data is the same between JVM instances. With class data sharing, multiple JVMs can share this common class data."
For example:
-Xshareclasses:cacheDir=/opt/share/classes
This connects the JVM to the shared cache directory at /opt/share/classes.
4. Setting the PermGen Size (-XX:PermSize)
-XX:PermSize defines the starting size of the Permanent Generation heap space. This holds Class and Method objects and the internalized String pool.
For example:
-XX:PermSize=512m
Many web applications load a large number of classes at deployment time. Increasing PermGen with -XX:PermSize avoids OutOfMemoryErrors.
Note: This option is obsolete since JVM 8 – the PermGen space is replaced by Metaspace which auto-sizes.
5. Logging Garbage Collection Details
These JVM options generate detailed logs on Garbage Collection activities:
-verbose:gc– Logs basic start and end messages.-XX:+PrintGCDetails– Adds information like size of generations.-XX:-PrintGCTimeStamps– Includes timestamp at each collection.
For example:
-verbose:gc -XX:+PrintGCDetails -XX:-PrintGCTimeStamps
This generates a rich GC log like:
[GC 2018-04-08T15:08:18.234-0400: 2.734: [ParNew: 6291456K->83000K(6291456K), 0.1672800 secs] 6291456K->851566K(12582912K), 0.1678930 secs]
The numbers provide invaluable insight into GC behavior to tune and troubleshoot latency issues caused by collection pauses.
6. Generating Heap Dumps on OutOfMemoryError
-XX:+HeapDumpOnOutOfMemoryError generates a heap dump when the JVM runs out of memory. This helps identify what‘s filling up the heap.
By default, the dump is written to the JVM‘s working directory. To customize:
-XX:HeapDumpPath=/var/log/dumps
Caution: Heap dumps can be huge – often tens of GBs in size! Ensure the target filesystem has adequate capacity.
This option is a must-have for production systems. It captures invaluable metrics before OOM kills the JVM.
7. Tracing Class Loading
-XX:+TraceClassLoading logs the name and details whenever a class loads into the JVM. This helps identify classloader issues:
[Loaded java.net.URLClassLoader$1 from shared objects file]
Similarly, -XX:+TraceClassUnloading logs when classes unload.
Used together, these flags help detect classes not being garbage collected properly – a sign of bugs or design issues.
8. Skipping Classpath Verification (-Xbootclasspath)
By default, the JVM verifies loaded classes to prevent issues like buffer overflows.
-Xbootclasspath allows specifying classpath entries to skip verification. For example:
-Xbootclasspath:/trusted/path/
This avoids expensive verification for trusted, safe classes. Often used for frameworks/APIs.
Use with caution – invalid classes here destabilize the JVM. Verify safety before adding to boot classpath.
9. Enabling Profiling (-Xprof)
Java profiling monitors JVM internals like method calls, object creation, thread execution times, and garbage collection. This helps identify hotspots, bottlenecks, and optimization opportunities.
-Xprof enables profiling support in the JVM. For example:
-Xprof -Xrunhprof
This starts the JVM with profiling enabled and loads the HPROF profiler agent.
Other profilers like VisualVM, JProfiler and YourKit also hook into -Xprof.
10. Setting the JVM Bit Version (-d32/-d64)
Modern operating systems support both 32-bit and 64-bit execution. By default, the JVM auto-detects the OS mode.
To explicitly control the JVM‘s bitness:
-d32– Runs the JVM in 32-bit mode-d64– Runs the JVM in 64-bit mode
For example:
-d64
Forces a 64-bit JVM even if 32-bit OS libraries are present. The -d32 or -d64 must match the JVM‘s binary architecture.
Bonus: Misconceptions and Bad Practices
Let‘s bust some common misconceptions around JVM options:
Myth: Bigger heap sizes always improve performance.
Truth: Beyond a point, excessively large heaps degrade GC performance and response times. Tune heap sizes based on data from load tests.
Myth: PermGen no longer needs tuning after JVM 8.
Truth: PermGen is replaced by Metaspace, but tuning is still vital to avoid OutOfMemoryErrors.
Bad Practice: Using huge thread stacks like -Xss16m.
Better Approach: Default thread stack size of 512K is adequate for most applications. Increase judiciously after data shows excessive stack usage.
Bad Practice: Using experimental options like -XX:+UseZGC in production.
Better Approach: First thoroughly test experimental options under load before rolling out. Use only stable options in production.
Conclusion
We‘ve explored the most essential JVM options for building and deploying Robust Java applications. Tuning the heap sizes, garbage collection, and enabling profiling data can profoundly impact the performance and stability of systems running large workloads.
Internalize the key options discussed here, but also study the many other useful options not covered in this guide. A strong grasp of JVM knobs and dials will serve any Java developer well.
With great optimization comes great responsibility! Always benchmark options under production load. And back up any production config changes for rollback.
Hopefully this guide has armed you with powerful knowledge to tune your Java apps. Go forth and optimize! Let me know if you have any other favorite JVM options not mentioned here.