Does Garbage Collector Choice Matters for Microservices?

When building and deploying microservices with Java — whether using Spring Boot or WildFly — one of the often-overlooked performance factors is the Garbage Collector (GC). Should you care about which GC your application uses? Or should you leave it to the infrastructure? Let’s break it down.

Why Garbage Collector Choice Matters

Garbage Collection is a core part of how the Java Virtual Machine (JVM) manages memory. The GC’s job is to clean up unused objects to free up memory, but how and when it does that can significantly impact your application’s performance.

In microservices, where responsiveness, stability, and resource efficiency are crucial, the GC can be the silent bottleneck if not configured properly. Here’s why:

1. Latency Spikes

Some GCs (like Parallel GC) may cause long application pauses. These pauses can increase response times, break SLAs, or even cause failures in health checks — especially in Kubernetes environments.

2. Throughput vs. Responsiveness

Different GCs optimize for different things. For example:

  • Parallel GC prioritizes throughput (processing as much as possible).
  • G1GC, ZGC, and Shenandoah focus on minimizing pause times — ideal for services handling frequent API requests.

3. Memory Efficiency

In containerized deployments, memory is usually limited. A GC that can handle memory fragmentation and pressure gracefully (like G1GC) is often a better fit.

4. Startup Time

In some microservices that scale quickly (e.g., under load), faster startup may matter. In those cases, GC behavior can also influence how fast a new instance becomes ready.


Popular GC Options (Java 21)

GCPause TimeThroughputNotes
G1GC (default)LowHighGood general-purpose choice for microservices.
ZGCVery LowModerateDesigned for ultra-low pause times, works well with large heaps.
ShenandoahVery LowModerateSimilar to ZGC, especially effective in Red Hat-based environments.
Parallel GCHighVery HighGood for batch processing, not ideal for APIs.
Serial GCHighLowOnly suitable for tiny apps or short-lived processes.

Spring Boot vs. WildFly: Who Chooses the GC?

Spring Boot

You have full control over the JVM. Whether you run it as a fat JAR or inside a Docker container, you decide the GC strategy. You can pass GC options via command line, JAVA_OPTS, or Dockerfile.

WildFly

WildFly (Jakarta EE) runs inside a managed JVM. But even here, you can (and should) configure the GC by editing the standalone.conf file or Docker entrypoint.

Cloud & Kubernetes Deployments

In containerized environments, infrastructure often sets memory limits, but it’s still your responsibility to:

  • Configure the GC explicitly.
  • Set memory-aware options like -XX:MaxRAMPercentage.
  • Monitor and tune based on GC logs (-Xlog:gc* in Java 21+).

Best Practices

  • Stick with G1GC unless you have specific latency or heap size needs.
  • Consider ZGC or Shenandoah for low-latency, large-memory services.
  • Avoid Parallel GC in APIs unless it’s a batch job.
  • Always monitor GC logs in production.
  • Tune JVM settings for container memory limits.

Final Thoughts

In microservices, small inefficiencies can scale into big problems. GC choice might seem like a low-level detail, but it has a direct impact on the performance, stability, and scalability of your services.

So yes — whether you’re using Spring Boot, WildFly, or any other Java stack, take a few minutes to understand and configure your Garbage Collector. Your uptime (and your users) will thank you.