JVM Performance for High-TPS Systems: Which JVM Is Fastest and How to Tune It

No JVM is fastest for every workload. This guide compares HotSpot, GraalVM, OpenJ9 and Azul Zing, explains how to choose a garbage collector from your latency target, and lists practical tuning steps for high-TPS Java services.

If your Java service must handle high transaction rates, the JVM and its garbage collector can affect throughput and tail latency as much as application code does. But there is no single fastest JVM. The right choice depends on whether you optimise for sustained throughput, p99 and p99.9 response times, startup time, or memory footprint.

TL;DR

  • No JVM wins every workload. Compare candidates on your own production-like load.
  • HotSpot is the default and the lowest-risk starting point. Its default collector, G1, is balanced for small, uniform pauses at high throughput.
  • Use ZGC when latency is the priority, and Parallel GC when peak throughput matters more than pause length.
  • GraalVM native image gives fast startup and immediate peak performance, but it changes the build and deployment model.
  • Set the heap size and pause goal first. Change JIT flags only after you have measurements.

What “fastest” means for a high-TPS service

Four measures usually matter, and they often pull in different directions:

  • Sustained throughput: operations per second on fixed hardware, after warm-up.
  • Latency under load: p99 and p99.9 response times, which garbage collection pauses and JIT compilation can distort.
  • Warm-up: how quickly the JVM reaches steady-state speed after a restart or deployment.
  • Footprint: memory and CPU used per instance, which determines how many instances you need.

Microbenchmarks rarely settle the question. A simple method-level test can rank runtimes differently from a service under real request mix, database calls and GC pressure.

The main JVM options

HotSpot (OpenJDK and Oracle JDK)

HotSpot is the reference implementation and has the broadest tooling, documentation and operational experience. Oracle’s guidance is to let the VM select a collector first, and only change it when the measured behaviour misses your goals.[1]

GraalVM (JIT and native image)

GraalVM’s native image compiles a Java application ahead of time into a native executable. It starts in milliseconds and provides peak performance without a warm-up phase.[2] Profile-guided optimisation can increase throughput for most native images, though the benefit depends on the workload.[3] The trade-off is a build step, reflection and configuration constraints, and a different debugging and profiling workflow.

OpenJ9

OpenJ9 focuses on smaller memory use and quicker startup. In a 2021 warm-up study, OpenJ9 was faster in the first few hundred iterations, but HotSpot and GraalVM overtook it at steady state.[4] In a database benchmark study, HotSpot delivered about 17% to 28% more throughput than OpenJ9 on a NoOp benchmark, and about 5% to 18% more on YCSB.[5] Check whether that gap holds for your version and workload before choosing OpenJ9 for throughput.

Azul Zing and Azul Prime

Azul’s commercial runtime is designed for consistent response times under load. Azul’s study reports lower maximum outliers than HotSpot in a Cassandra test at several throughput levels.[6] That is a vendor-run study, so treat it as a hypothesis to test. Azul’s own evaluation guidance recommends running the same production-like test on each JDK, changing only the JDK path, for at least an hour.[7]

Microbenchmark evidence needs caution

A single Fibonacci-call microbenchmark ranked GraalVM Enterprise first, with Zing about 12 percentage points behind it and HotSpot 11 ahead of HotSpot 8.[8] Use results like this to understand compiler behaviour, not to predict your service’s TPS.

Option Strength Trade-off Evidence to check
HotSpot, G1 (default) Balanced pauses, broad tooling Not the lowest-latency option Your own p99 under load
HotSpot, Parallel GC High throughput Longer pauses are acceptable Pause times against your SLA
HotSpot, ZGC Sub-millisecond pause goals Some throughput cost Throughput at your latency target
GraalVM native image Fast startup, no warm-up Build constraints, configuration Peak throughput after PGO
OpenJ9 Lower memory, quicker early warm-up Lower steady-state throughput in the cited studies Steady-state results on your workload
Azul Zing or Prime Response-time consistency Commercial licence Vendor claims versus your own test

Choosing a garbage collector

Oracle’s collector guidance is to choose based on the response-time need:

  • Parallel GC (-XX:+UseParallelGC) suits peak throughput when pauses of one second or longer are acceptable.[1]
  • G1 (-XX:+UseG1GC) suits services where response time matters more than overall throughput, while keeping pauses short.[9]
  • ZGC (-XX:+UseZGC) suits services with a high priority on response time. Its pause times stay below a millisecond and do not depend on heap size, at some cost to throughput.[1] Since JDK 23, ZGC uses its generational mode by default.[10]

JVM tuning tips for high-TPS services

  1. Measure before tuning. Run a production-like load for at least an hour, and record business metrics such as p99 response time, throughput and timeouts.[7]
  2. Start with defaults. Oracle recommends G1 with its default settings, a pause-time goal if needed, and a maximum heap set with -Xmx.[9]
  3. Set the heap deliberately. For ZGC, the maximum heap size is the most important option.[11] Setting the initial heap equal to the maximum (-Xms equal to -Xmx) can avoid resize pauses. Confirm this on your workload, because it also reserves memory up front.
  4. Tune G1 for the goal you have. For throughput, relax -XX:MaxGCPauseMillis or add heap. For latency, tighten the target and watch whether mixed collections take too long.[9]
  5. Check G1 humongous objects. Large allocations can fragment the heap. Oracle’s G1 guide covers this case in its latency-tuning section.[9]
  6. Warm up before you measure. JIT compilation differs by runtime. Excluding warm-up from results can mislead you about steady-state throughput.[4]
  7. Change one variable at a time. Keep the JDK version, hardware and load profile fixed while you compare collectors or flags.[7]
  8. Use native image where startup is the constraint. Scale-out and short-lived workloads often benefit. Test PGO and build configuration before committing to it for long-running throughput.[3]

Recommendation

Start with the current HotSpot LTS release and G1 defaults, set -Xmx, and measure p99 under production-like load. If pause time is the limiting factor, test ZGC. If peak throughput matters and pauses are tolerable, test Parallel GC. Evaluate GraalVM native image when startup time or footprint is the constraint. Evaluate OpenJ9 or Azul only with your own workload, because the published comparisons are narrow or vendor-run.

Frequently asked questions

Is GraalVM always faster than HotSpot?

No. Results depend on the workload. One microbenchmark favoured GraalVM, but the evidence does not support a general claim.[8]

Which garbage collector should I use for low latency?

ZGC is built for low pause times. Test it against your p99.9 target, and check the throughput cost at your load.[1]

Sources

  1. Oracle, Available Collectors (JDK 25 HotSpot GC Tuning Guide)
  2. GraalVM, Native Image reference manual
  3. GraalVM, Optimizations and Performance
  4. Kriegl, Benchmarking the Warm-Up Performance of HotSpot VM, GraalVM and OpenJ9 (Conf42, 2021)
  5. Tell-Tale Tail Latencies: Pitfalls and Perils in Database Benchmarking (arXiv:2107.11607, 2021)
  6. Azul, JVM Performance Study: Zing vs Oracle HotSpot (vendor study, undated)
  7. Azul, Best Practice: Comparative Evaluation of JDK Setups
  8. k7a.io, Comparing performance of JVM implementations (single microbenchmark)
  9. Oracle, Garbage-First Garbage Collector Tuning (JDK 21 HotSpot GC Tuning Guide)
  10. OpenJDK, JEP 474: ZGC: Generational Mode by Default
  11. Oracle, HotSpot Virtual Machine Garbage Collection Tuning Guide (JDK 25 PDF)
Subscribe
Notify of

0 Comments
Oldest
Newest Most Voted