Healthy vs Non-Healthy Garbage Collection Behavior In The JVM
Introduction
Ever watched your Java service slow to a crawl for no clear reason?
Often, the culprit isn’t your code — it’s your garbage collector gasping for air.
Garbage collection (GC) is the JVM’s built-in memory management mechanism, automatically reclaiming space from unreachable objects so you can focus on writing logic instead of worrying about leaks.
But GC activity is more than just a background process — it’s like your application’s heartbeat. Smooth, predictable GC behavior signals healthy memory use, while erratic or frequent GC events often reveal deeper performance issues: leaks, excessive allocation, or poor tuning.
In this post, we’ll explore what defines healthy vs. unhealthy GC behavior, how to interpret heap usage patterns, and how to correct GC issues before they impact performance.
What does "healthy" GC behavior look like
When the GC is functioning optimally, you'll see a stable rhythm of memory allocation and reclamation.
Typical Pattern
- The heap gradual fills as the application allocates objects.
- A GC event (young or full) reclaims unused memory.
- Heap usage drops, and the cycle repeats predictably.
This creates the characteristic "saw-tooth" pattern in heap usage charts - often a sign of a healthy application.
"Heap usage will keep rising; once a Full Garbage collection ... occurs memory utilization drops all the way to the bottom ... It indicates that the application is in a healthy state & not suffering from any sort of memory problems." DZone: Interesting Garbage Collection Patterns
Traits of a Healthy GC
- GC events are infrequent and quick.
- Application throughput remains high.
- Post-GC heap usage (the "baseline") stays consistent over time.
Note: With modern concurrent collectors like G1GC, ZGV, and Shenandoah, the saw-tooth pattern may appear smoother since these collectors perform most work concurrently, reducing large visibility drops.
What does "Unhealthy" GC behavior Look Like?
Unhealthy GC behavior indicates that your application or JVM configuration is struggling to manage memory efficiently.
Common Symptoms
| Symptom | Description | Likely Causes |
|---|---|---|
| Frequent Full GCs | Full GCs happen seconds apart, consuming CPU and causing pauses | Heap to small, excessive allocation, old-gen promotion pressure. |
| Rising Heap Baseline | Memory after GC keeps growing after GC. | Objects are still strongly referenced. |
| GC Thrashing | JVM spends more time in GC than running the app. | Small heap, too amny short-lived objects, mis-tuned GC parameters |
| Promotion Failures / OOM | Objects move prematurely to old generation, eventually OOM | Survivor space exhaustian, high allocation churn. |
Visual Examples: Healthy vs Unhealthy Patterns
| Pattern | Description | Typical Cause |
|---|---|---|
| Heap rises and falls predictably with GC cycles | Balanced allocation and reclaimation | |
| Each GC leaves more memory retained | Memory leak or poor caching logic. | |
| Heap stays high even after GC | Retained strong references. | |
| Constant GCs reclaim little memory | Heap too small or allocation too fast |
Diagnosing GC Health
Understanding GC health requires monitoring metrics, and log analysis.
GC Logs
Enables GC logging in modern JVMs:
java -Xlog:gc*:file=gc.log:time,uptime,level,tags MyApp
| Parameter | Description |
|---|---|
java |
Runs the Java Virtual Machine (JVM). |
-Xlog:gc* |
Enables detailed logging for all garbage collection events. |
file=gc.log |
Writes logs to a file (gc.log) for later analysis. |
time |
Includes wall-clock timestamp of each event. |
uptime |
Adds time since JVM startup. |
level |
Includes log severity (info, warning, etc.). |
tags |
Includes event tags (gc, heap, pause, etc.). |
MyApp |
The Java class containing the main method to run. |
Monitoring Tools
| Tool | Description |
|---|---|
| VisualVM | live heap and GC charts |
| Java Mission Control (JMC) | Advanced Profiling And Flight Recordings |
| Grafana & Prometheus | Dashboards |
Key Metrics to Watch
| Metric | What It Tells You | Health Range / Guidance |
|---|---|---|
| GC Pause Time | Duration of stop-the-world events. | <200ms for latency specific systems. |
| GC Frequency | How ofen the GCs occur | Should scale with allocation rate, not spike randomly. |
| Heap after GC | Post-collection heap usage. | Should stay roughly stable over time. |
| Allocation Rate | Object creation speed | Excessive rates -> rune caching or reuse objects |
Keeping Your GC Healthy
Right-Size Your Heap
- Set
-Xmsand-Xmxto match actual usage. - Avoid setting them too small - causes frequent GCs.
- Avoid excessive heap - delays leak detection.
Pick the Right GC Algorithm
- G1GC (default since JDK 9): Balanced throughput and latency.
- ZGC / Shenandoah: Ultra-low pause times, ideal for large heaps.
- Parallel GC: Best for batch jobs with high CPU throughput.
Reduce Object Churn
- Reuse objects and buffers
- Use primitive collections (eg. TIntArrayList) to reduce boxing.
- Avoid temporary object floods (e.g., string concatenation inside loops).
Profile and Analyze Regularly
- Use memory profiles (JMC, Eclipse MAT) to spot leaks.
- Investigate any rising heap baselines early.
Example: Diagnosing a Leak via GC Pattern
- Heap baseline keeps rising every hour.
- GC logs show increasing "used after GC" values.
- Heap dump analysis reveals a large ConcurrentHashMap holding cached objects with no eviction policy.
- Fix -> Add cache expiration -> GC returns to steady saw-tooth-pattern.
Conclusion
Health garbage collection isn't about minimizing GC activity. It's about maintaining a predictable, balanced cycle where memory is reclaimed efficiently and the application spends most time doing real work.
When GC becomes erratic, too frequent, or too ineffective, it's a sign to:
- Reassess heap sizing and GC algorithm choice.
- Analyze object allocation patterns.
- Fix leaks and optimize caching.
With consistent monitoring and tuning, we can keep the JVM's "heartbeat" steady - ensuring high throughput and long-term stability.
Further Reading
- Oracle GC Tuning Guide
- Datadog: Understanding Java GC
- Papertrail: 7 GC Log Problems to Look Out For
- StackOverflow: Memory Usage Patterns & Leaks
- StackOverflow: JVM Heap Usage Variation
---
title: Healthy vs Non-Healthy Garbage Collection Behavior In The JVM
description: What defines healthy versus unhealthy GC behavior, how to interpret heap usage patterns, and how to correct GC issues before they impact performance?
date: 2025-10-28
keywords: [java,jvm]
---