3 Essential Grafana Panels for Monitoring JVM Heap and Memory Spikes
The three critical PromQL panels every platform engineer needs to diagnose memory leaks, GC pauses, and heap spikes in enterprise Java applications.
Running enterprise Java applications—such as Jira, Confluence Data Center, SonarQube, or custom Spring Boot services—on Kubernetes requires continuous observability into the Java Virtual Machine (JVM).
In containerized environments, simply monitoring container memory (container_memory_working_set_bytes) is deceptive:
- The JVM process naturally grabs memory up to
-Xmx(plus metaspace, code cache, and native memory). - A container can appear to consume 80% to 90% of its memory limit while the internal JVM heap is actually healthy and idling at 40%.
- Conversely, a service can suffer catastrophic Stop-the-World garbage collection pauses while overall container memory looks stable.
Using the standard Prometheus JMX Exporter, here are the three essential Grafana panels you need to diagnose memory pressure, sudden heap spikes, and thread contention.
Panel 1: JVM Heap Memory (Used vs. Committed vs. Max)
This panel answers: Is the JVM running out of heap space, or is it behaving normally within its allocated boundaries?
In a healthy JVM, heap usage follows a classic "sawtooth" pattern: memory builds up as objects are allocated, and drops sharply whenever minor garbage collections run. A typical steady state might sit around 50% to 55% heap utilization, with periodic spikes reaching 80% to 90% during heavy operations (e.g., bulk exports, complex indexing, or large report generations).
PromQL Queries
# 1. Used Heap Memory
jvm_memory_bytes_used{area="heap"}
# 2. Committed Heap Memory (Allocated by the OS to the JVM)
jvm_memory_bytes_committed{area="heap"}
# 3. Maximum Heap Limit (-Xmx)
jvm_memory_bytes_max{area="heap"}
What to Look For
- Healthy Sawtooth: Frequent dips back down to the baseline (e.g. ~50%).
- Memory Leak Warning: The baseline at the bottom of the trough steadily creeps upward over hours or days without resetting after Full GC.
- Immediate Crash Risk: Used memory approaches within 5% of
maxand fails to recover, causingjava.lang.OutOfMemoryError: Java heap space.
Panel 2: Garbage Collection Duration & Pause Times
This panel answers: Is garbage collection degrading application response time or freezing client requests?
When the JVM struggles for memory, it spends increasing CPU cycles attempting to reclaim space. If it triggers consecutive Full GC (Stop-The-World) pauses, every worker thread is paused, health-check probes fail, and ingress controllers report 502 Bad Gateway or 504 Gateway Timeout.
PromQL Queries
# Rate of time spent in GC pauses (seconds spent per second)
sum by (action) (rate(jvm_gc_collection_seconds_sum[1m]))
# Total GC collection count rate (frequency of runs)
sum by (action) (rate(jvm_gc_collection_seconds_count[1m]))
What to Look For
- Normal Operation: Short, frequent young-generation collections (e.g., G1 Young Generation) with pause times under 50–100ms.
- Dangerous Thrashing: The rate of GC collection time spikes above 0.3s/sec. If a service spends 30% or more of its runtime executing GC, throughput collapses.
- Full GC Freezes: Look for spikes in
action="end of major GC"oraction="end of full GC". Any pause lasting multiple continuous seconds demands heap reallocation or memory leak investigation.
Panel 3: Active and Peak JVM Threads
This panel answers: Is the application stalling because threads are deadlocked, waiting on external dependencies, or exhausting thread pools?
Memory issues and thread exhaustion often travel together. When slow downstream databases or blocked socket I/O prevent threads from finishing, each thread holds its local stack and referenced objects in heap memory. This leads to simultaneous thread explosion and memory spikes.
PromQL Queries
# 1. Currently Live / Active Threads
jvm_threads_current
# 2. Peak Thread Count
jvm_threads_peak
# 3. Deadlocked Threads (Crucial Alert Trigger)
jvm_threads_deadlocked
What to Look For
- Thread Plateaus: A steady increase in
jvm_threads_currentthat matches an upstream connection timeout or slow SQL query. - Deadlock Indicator:
jvm_threads_deadlocked > 0requires an immediate thread dump (jstackor via diagnostic tooling) to find lock contention points. - Thread Starvation: If threads hit configured web server maximums (e.g., Tomcat/Jetty worker limits), incoming requests are dropped before entering application business logic.
Recommended Prometheus Alert Rules
To catch issues before users report outages, establish two core alerts based on these panels:
groups:
- name: jvm_alerts
rules:
- alert: JVMHeapPressureHigh
expr: (jvm_memory_bytes_used{area="heap"} / jvm_memory_bytes_max{area="heap"}) > 0.90
for: 5m
labels:
severity: warning
annotations:
summary: "JVM heap usage exceeds 90% for more than 5 minutes."
- alert: JVMHighGCPauseTime
expr: rate(jvm_gc_collection_seconds_sum[2m]) > 0.3
for: 2m
labels:
severity: critical
annotations:
summary: "JVM is spending >30% of its runtime in garbage collection."
Summary
Container metrics alone only tell you what the Linux kernel sees. By monitoring internal JVM heap usage, garbage collection pause frequency, and live thread counts via Prometheus JMX Exporter, you can accurately identify performance degradation, size Kubernetes memory limits correctly, and prevent production out-of-memory outages.
Technical Discussion (0)
Ask a question or reply to a thread below. Your email is never displayed publicly.
No comments yet. Start the technical discussion above.