Skip to content

AxonOps — AI-Native Control Plane for Open Source Data Platforms

nodetool gcstats

Displays garbage collection statistics for the Cassandra JVM.


Terminal window
nodetool [connection_options] gcstats [options]

See connection options for connection options.

OptionDescription
-F, --format <format>Output format: json, yaml, or table (default)
-H, --human-readableDisplay sizes in human-readable format

nodetool gcstats shows cumulative garbage collection metrics including:

  • GC invocation counts
  • Total GC time
  • Time since last GC

Collection that takes an increasing share of node time raises request latency and, at the extreme, stops the node responding to peers. The values come from the JVM garbage collection MXBeans over JMX (java.lang:type=GarbageCollector) rather than from Cassandra's own metrics, so they report what the JVM observed rather than what Cassandra measured.

Elapsed time is not stop-the-world pause time

The elapsed time gcstats reports is the total time the collector was active, which is not the time application threads were stopped. G1, Shenandoah and ZGC do much of their work alongside application threads, so their elapsed time covers concurrent phases as well as pauses; a fully stop-the-world collector is the only case in which the two are equal. Use the GC log for pause durations, and read gcstats as a measure of collection pressure over an interval.


Interval (ms) Max GC Elapsed (ms)Total GC Elapsed (ms)Stdev GC Elapsed (ms) GC Reclaimed (bytes) GC Count Allocated Direct Memory Bytes Max Direct Memory Bytes Reserved Direct Memory Bytes
986 123 45678 45 129536000 1234 67108864 134217728 134217728

FieldDescription
Interval (ms)Time since gcstats was last called
Max GC Elapsed (ms)Longest single GC pause
Total GC Elapsed (ms)Cumulative GC time
Stdev GC Elapsed (ms)Standard deviation of GC times
GC Reclaimed (bytes)Memory reclaimed by GC (in bytes; use -H for human-readable units)
GC CountNumber of GC invocations
Allocated Direct Memory BytesCurrently allocated off-heap (direct) memory
Max Direct Memory BytesMaximum off-heap memory configured
Reserved Direct Memory BytesReserved off-heap memory

Max GC Elapsed (ms)Total GC Elapsed (ms) Collections
50 5000 1000

Expect Max GC Elapsed to sit below the pause goal set for the running collector. 5.0 sets -XX:MaxGCPauseMillis=300 for G1, and 4.1 and 4.0 under G1 set 300 and 500 respectively; CMS, which 4.1 and 4.0 start with, has no goal, so the comparison there is against the baseline established for the node under representative load. Monitoring thresholds per version gives the figure for each release and the setting it derives from.

Max GC Elapsed (ms)Total GC Elapsed (ms) Collections
2000 500000 50000

Pressure shows as Max GC Elapsed repeatedly above the goal for the running collector, as a collection count rising while the workload is unchanged, and as average elapsed time per collection trending upward across intervals. The last of those needs successive samples, since one call reports a single interval.

Max GC Elapsed (ms): 2345

A pause approaching read_request_timeout or write_request_timeout in cassandra.yaml (read_request_timeout_in_ms and write_request_timeout_in_ms on 4.0) fails the requests in flight on the node, whatever the collector. A pause long enough to stop the node answering gossip causes peers to mark it down, after which hints accumulate and coordinators route around it until it returns. Both consequences follow from the pause duration against those timeouts rather than from a fixed number of milliseconds.


Terminal window
# Check GC health periodically
nodetool gcstats

When experiencing slow queries:

Terminal window
nodetool gcstats
nodetool tpstats
nodetool proxyhistograms

Verify GC behavior after JVM changes:

Terminal window
# Before change
nodetool gcstats > gc_before.txt
# Make JVM changes, restart
# After change
nodetool gcstats > gc_after.txt
diff gc_before.txt gc_after.txt

Monitor GC under stress:

Terminal window
watch -n 5 'nodetool gcstats'

Terminal window
# Take samples
nodetool gcstats
sleep 60
nodetool gcstats

The difference shows GC activity in that interval:

MetricSample 1Sample 2DeltaInterpretation
Collections1000105050~1 GC/sec
Total GC500070002000ms2 sec GC in 60 sec
GC %--3.3%Acceptable
GC overhead = (Total GC Elapsed / Node Uptime) × 100
OverheadAssessment
< 5%Normal
5-10%Monitor closely
> 10%Tuning needed
> 20%Critical

High collection count with large Max GC indicates full GCs:

Causes:

  • The heap is too small for the live set the workload retains
  • An object is held past its use, so the live set grows across cycles
  • Individual allocations are large enough to bypass the young generation, which under G1 means humongous allocations occupying whole regions

Solutions:

  • Check what is retained on the heap before raising it: a larger heap without a change in allocation moves the failure later and lengthens each collection
  • Review the workload for the usual sources of retention, large partitions and unbounded reads
  • Where the heap is undersized, compare it against the value the running release computes for the host (heap sizing per version), since a heap below that figure has been pinned by hand

Causes:

  • A stop-the-world collection traces the whole heap in one pause, which is what CMS falls back to on 4.1 and 4.0 when concurrent collection cannot keep up
  • The old generation has no room for objects surviving a young collection, so promotion fails and a full collection follows
  • The live set itself is large, so every cycle traces and copies more of it

Solutions:

  • Reduce the allocation rate first: large partitions, unbounded reads and oversized batches produce most of it
  • Confirm the collector matches the release, G1 on 5.0 and CMS on 4.1 and 4.0, since a hand-configured collector explains behaviour the shipped settings do not
  • Change one garbage collector setting at a time, against a pause profile measured over representative load

Low GC Reclaimed despite high collections:

Causes:

  • Most of what the heap holds is long-lived, such as on-heap caches and memtables, so a collection finds little to reclaim
  • An object is held past its use, so the live set grows across cycles and does not fall back
  • The node is short of memory off the heap rather than on it, which no heap collection can relieve

Solutions:

  • Compare the key cache and row cache sizes against the heap, since on-heap cached data is live data by design (Cassandra memory structures)
  • Take a heap dump and identify what holds the largest retained sets before changing any collector setting
  • Read off-heap usage with nodetool info | grep "Off Heap Memory", since a node under off-heap pressure shows the same low reclaim figure and responds to none of the heap changes above

The collector and its settings ship active in the per-JDK options file and do not need adding. The heap is computed by cassandra-env.sh at start rather than written in an options file, and GC logging is already enabled, writing to /var/log/cassandra/gc.log in 10 rotating files of 10MiB.

Heap sizing and garbage collector settings per version gives the full set of shipped values, what changing each one costs, and how to measure the result.

Use tools like:

  • GCViewer
  • GCEasy
  • VisualVM

Terminal window
nodetool gcstats
nodetool tpstats

High GC often correlates with:

  • Blocked threads
  • Pending tasks
  • Dropped messages
Terminal window
nodetool gcstats
nodetool proxyhistograms

GC pauses directly impact p99 latencies.

Terminal window
nodetool gcstats
nodetool info | grep "Heap Memory"

Near-full heap triggers more frequent GC.


Terminal window
nodetool gcstats
Terminal window
watch -n 10 'nodetool gcstats'
Terminal window
for node in node1 node2 node3; do
echo "=== $node ==="
ssh "$node" "nodetool gcstats"
done
#!/bin/bash
# Threshold is the pause goal the running collector was given: 300 under G1 on
# 5.0. Under CMS on 4.1 and 4.0 there is no goal, so use the baseline measured
# for the node instead.
threshold=300
max_gc=$(nodetool gcstats | tail -1 | awk '{print $2}')
if [ "$max_gc" -gt "$threshold" ]; then
echo "WARNING: Max GC elapsed ${max_gc}ms exceeds ${threshold}ms"
fi

nodetool gcstats reports an interval, so a single call establishes nothing. Take a baseline over a period of representative load, keep the reading in continuous monitoring with history and alerting rather than calling the command by hand, and correlate pause durations against read and write latency before concluding that collection is the cause of a latency spike. GC logging is already enabled on 5.0, 4.1 and 4.0 and gives the per-collection detail this command summarises.

Heap size and collector are not changed in response to this output alone. JVM Tuning gives the shipped values per version, which settings are worth changing, and the measurement each change should be made against.


CommandRelationship
infoHeap memory usage
tpstatsThread pool status
proxyhistogramsLatency histograms
tablestatsTable metrics