Back to JVM Tuning & Troubleshooting
Q4Question 4 of 15

What are the main garbage collectors?

Key Concepts
  • Serial: single-thread, small heaps, client apps
  • Parallel: multi-thread throughput collector
  • G1 (default since Java 9): region-based, predictable pauses
  • ZGC: sub-millisecond pauses, large heaps (terabytes)
  • Shenandoah: concurrent compaction, low latency

Definition

Serial for tiny apps, Parallel for throughput, G1 for balanced low pauses (the modern default), ZGC and Shenandoah for ultra-low pauses on huge heaps.

The JVM ships with several garbage collectors, each making a different trade-off between pause time, throughput, and CPU usage. Serial GC uses a single thread and is suited to small applications. Parallel GC uses multiple threads to maximize throughput and was the Java 8 default. G1 GC divides the heap into regions and targets predictable low pause times, and it is the default from Java 9. ZGC and Shenandoah are concurrent collectors that keep pauses in the low single-digit milliseconds even on very large heaps.

Understanding the Concepts

What are the main garbage collectors?

The JVM ships with several garbage collectors, each optimized for a different workload. Choosing the right one is one of the most impactful tuning decisions you can make.

Serial GC

Serial GC uses a single thread for both Minor and Major collections and stops all application threads while it runs. Because it has no threading overhead, it is efficient for small heaps and single-core or constrained environments.

BASH
java -XX:+UseSerialGC -Xmx512m -jar bank-tool.jar

It is a good fit for small command-line tools, tiny microservices with little memory, or containerized jobs with a single vCPU.

Parallel GC

Parallel GC, also called the throughput collector, uses multiple threads to perform Minor and Major collections faster, shortening the stop-the-world pauses compared to Serial. It optimizes for maximum throughput, meaning the application does the most useful work per unit of CPU, accepting that pauses can be longer when they do happen. It was the default up to Java 8.

BASH
java -XX:+UseParallelGC -XX:ParallelGCThreads=8 -Xmx8g -jar interest-batch.jar

It suits batch jobs and analytics where total completion time matters more than individual pause length.

G1 GC

G1 GC (Garbage First) is the default from Java 9. It divides the heap into many equal-sized regions rather than fixed Young and Old areas, and it collects the regions containing the most garbage first. It does much of its marking work concurrently and aims to meet a pause-time goal you set with -XX:MaxGCPauseMillis.

BASH
java -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -Xmx4g -jar bank-api.jar

It gives a good balance of throughput and predictable pauses, which is why it is the general-purpose default for most server applications.

ZGC

ZGC is a low-latency collector that keeps pauses below roughly ten milliseconds, and in modern versions below one millisecond, regardless of heap size. It does marking and compaction concurrently with the application using colored pointers and load barriers.

BASH
java -XX:+UseZGC -Xmx32g -jar trading-engine.jar

The trade-off is extra CPU spent on barriers and slightly lower peak throughput.

Shenandoah

Shenandoah is similar to ZGC in goals: concurrent marking and concurrent compaction for low-pause collection. It uses a different implementation technique (Brooks forwarding pointers) but achieves similar results.

BASH
java -XX:+UseShenandoahGC -Xmx16g -jar payment-gateway.jar

Comparison table

| Collector | Threads | Default in | Pause time | Best for |
|---|---|---|---|---|
| Serial | Single | – | Longest | Small tools, single-core |
| Parallel | Multi | Java 8 | Medium | Batch, throughput |
| G1 | Multi + concurrent | Java 9+ | Configurable target | General server |
| ZGC | Concurrent | – | Sub-millisecond | Ultra-low latency |
| Shenandoah | Concurrent | – | Low milliseconds | Low latency |

How do you choose?

There is no universally best collector. You pick based on your service's priority.

JAVA
package com.bank.ops;

/**
 * Collector selection guide for banking workloads:
 *
 * 1. Overnight interest batch (throughput matters)  → Parallel GC
 * 2. Customer-facing API (balanced latency)          → G1 GC
 * 3. Real-time trading engine (strict latency)       → ZGC
 * 4. CLI migration tool (small heap, simple)         → Serial GC
 */
public class GCSelectionGuide {
    // Validate the choice with GC logs and load testing, not by guessing
}

Always validate with GC logs and load tests. Compare P99 and P999 pause times rather than averages, because averages hide the worst-case pauses that affect customer experience.

Key Takeaway

The JVM offers several garbage collectors, and each one makes a different trade-off between pause time, throughput, and CPU cost. Serial GC uses a single thread and stops the whole application while it collects, which is perfectly fine for small applications and constrained environments. Parallel GC, which was the default up to Java 8, uses many threads to collect quickly and is tuned for maximum throughput, so it suits batch and analytics workloads where total completion time matters more than any single pause. G1 GC, the default from Java 9 onward, splits the heap into many regions, collects the ones with the most garbage first, and lets you set a target maximum pause time, giving a balanced behavior that fits most server applications. ZGC and Shenandoah go further by doing almost all their work, including compaction, concurrently with the application, keeping pauses in the low single-digit milliseconds even on very large heaps.

The key insight I always bring up is that there is no single best collector. The right choice depends entirely on what the service cares about most. If it is raw throughput on a batch job, Parallel GC. If it is balanced, predictable behavior for a general online service, G1. If it is strict, consistent low latency for something like real-time trading, ZGC or Shenandoah. And you never choose by intuition alone; you validate with GC logs and load tests.

In my banking work at National Bank of India, we use exactly this split. Our overnight interest calculation batch runs under Parallel GC because nobody is waiting and we just want it finished fast. Our main customer-facing API runs under G1 with a pause-time target so the application never freezes long enough for a customer to notice. And our real-time trading engine runs under ZGC, because during market hours a long GC pause could mean a missed trade and real financial loss, so we accept a little extra CPU usage in exchange for pauses that stay around a millisecond every single time. Choosing the collector deliberately for each workload is one of the most impactful tuning decisions I make.

Remember one sentence: What are the main garbage collectors: Serial GC (single-threaded, for small apps), Parallel GC (multi-threaded, Java 8 default, good throughput), G1 GC (Java 9+ default, region-based, targets low pause times), ZGC (ultra-low pause <10ms, Java 11+), Shenandoah (low pause, concurrent) is fundamental knowledge for Java interviews and production banking work.

---

Share
Use ← → arrow keys to navigate