From jvm
Analyze JVM garbage-collection behavior, compare collector tradeoffs, and interpret pause-time and heap-pressure evidence. Use this skill when the user asks to "analyze GC logs", "compare G1 and ZGC", "understand Java garbage collection", "debug pause times", "explain heap pressure", or needs guidance on JDK garbage-collection diagnostics and collector tradeoffs.
npx claudepluginhub ririnto/sinon --plugin jvmThis skill uses the workspace's default tool permissions.
Use this skill to analyze garbage-collection symptoms, collector choices, and runtime evidence using standard JDK and HotSpot tooling. The common case is not switching collectors immediately; it is proving whether GC is actually the bottleneck, then comparing realistic collector options against the deployed Java baseline. Focus on pauses, throughput, allocation pressure, heap behavior, and what...
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
Use this skill to analyze garbage-collection symptoms, collector choices, and runtime evidence using standard JDK and HotSpot tooling. The common case is not switching collectors immediately; it is proving whether GC is actually the bottleneck, then comparing realistic collector options against the deployed Java baseline. Focus on pauses, throughput, allocation pressure, heap behavior, and what is actually available on the running JDK before touching tuning folklore.
Treat JDK 8, 11, 17, 21, and 25 as the supported LTS reference line for this skill, and anchor collector guidance to the actual deployed runtime instead of assuming the newest LTS behavior.
Treat JFR-based GC evidence as the normal low-overhead path on JDK 11 and later. On JDK 8, verify the exact Oracle JDK 8 Flight Recorder availability, commercial-feature status, and licensing posture before recommending JFR commands; otherwise prefer GC logs plus jcmd evidence first.
jcmd, GC logs, or JFR.For a running JVM, confirm the process and current runtime first:
jcmd -l
jcmd <pid> VM.version
jcmd <pid> VM.flags
For the next deploy, enable bounded GC log output for basic pause visibility:
java -Xlog:gc:file=gc-%p-%t.log:uptimemillis,pid:filecount=5,filesize=10M ...
Version-specific logging syntax — JDK 8 and earlier use legacy GC log flags (-verbose:gc, -XX:+PrintGCDetails, -XX:+PrintGCTimeStamps, -Xloggc:gc.log); JDK 9 and later use unified logging with -Xlog:gc....
Start with the smallest GC evidence set:
jcmd -l
jcmd <pid> VM.flags
jcmd <pid> VM.command_line
jcmd <pid> GC.heap_info
jcmd <pid> JFR.start name=gc-baseline settings=default disk=true maxage=2h
Use when: you have a live JVM and need first-line GC evidence before discussing collectors.
Live JVM GC first pass:
jcmd <pid> VM.version
jcmd <pid> VM.command_line
jcmd <pid> VM.flags
jcmd <pid> VM.flags -all
jcmd <pid> GC.heap_info
jcmd <pid> GC.class_histogram
Use when: the symptom is heap pressure, memory growth, or unexplained GC churn.
Low-overhead JFR GC recording:
jcmd <pid> JFR.start name=gc-baseline settings=default disk=true maxage=2h
jcmd <pid> JFR.check
jcmd <pid> JFR.dump name=gc-baseline filename=/path/to/private-diagnostics/gc-baseline-%p-%t.jfr
Use when: the pause or latency problem depends on runtime behavior over time.
Startup-attached GC-oriented JFR:
java -XX:StartFlightRecording=name=gc-startup,settings=profile,filename=/path/to/private-diagnostics/gc-startup.jfr,dumponexit=true -Xlog:gc=debug:file=gc-%p-%t.log:uptimemillis,pid:filecount=5,filesize=10M -jar app.jar
Use when: you need GC and allocation evidence from process start, not only after a later live attach.
[!IMPORTANT] GC-oriented JFR captures and GC log files can still expose sensitive runtime details. Prefer a private diagnostics directory with restrictive permissions, and keep retention only as long as the investigation needs.
On JDK 8, do not present JFR as the routine default. Confirm Oracle JDK 8 Flight Recorder availability and policy first, or stay with GC logs plus
jcmdevidence when that is uncertain.
Bounded GC logging for the next deploy:
java -Xlog:gc=debug:file=gc-%p-%t.log:uptimemillis,pid:filecount=5,filesize=10M ...
Use when: you cannot diagnose the current issue from existing captures and need better next-run evidence.
Legacy JDK 8 GC logging:
java -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:gc.log ...
Use when: the deployed runtime is JDK 8 or earlier and -Xlog is not available.
Startup-time collector verification:
java -XX:+PrintCommandLineFlags -version
Use when: you are validating a launch configuration or checking whether a proposed collector flag is actually accepted.
Unified logging inspection:
jcmd <pid> VM.log list
Use when: you need to know whether GC logging is already active and at what level.
Collector comparison checklist:
1. Confirm the active Java LTS baseline.
2. Confirm the active collector.
3. Decide whether the goal is pause time, throughput, or footprint.
4. Compare the default collector against alternatives only after reading evidence.
Use when: the user asks "Should we switch from G1 to ZGC?" or a similar collector-choice question.
Validate the common case with these checks:
GC.heap_info Outputjcmd <pid> GC.heap_info
Sample output (G1 on JDK 21):
Min heap alignment: 4096 KiB
Reserved region:
- [0x00000007ffc00000, 0x0000000800000000): committed
Heap region sizes:
- Region 0: 2048 KiB
- Region 1: 1024 KiB
G1 Heap:
num_regions: 512
Max regions: 512
Heap size: 1048576K
Free regions: 200
Young regions: 180
Eden regions: 160
Survivor regions: 20
Old regions: 132
Humongous regions: 0
Start CS time: 12345 ms
End CS time: 12346 ms
Read: Total heap = 1048576K (~1GB), 200 free regions (39% free). Young gen = 180 regions (35%), Old gen = 132 regions (26%). If Free regions drops below ~50 during sustained load, the heap is under pressure.
VM.flags Output (Collector Identity)jcmd <pid> VM.flags
Look for these flag lines to identify the active collector:
-XX:G1HeapRegionSize=4M ← G1 is active
-XX:+UseG1GC ← G1 explicitly set (or default)
-XX:+UseZGC ← ZGC is active
-XX:+UseParallelGC ← Parallel GC is active
-XX:+UseSerialGC ← Serial GC is active
If none of these appear, the default collector for that LTS applies (G1 for 11+, Parallel for 8 server-class).
JFR.check Outputjcmd <pid> JFR.check
Sample output:
Recording 1: name=gc-baseline maxsize=0 (unlimited) duration=0 (unlimited) disk=true
Recording: to=true size=48 KiB (48 KiB) maxsize=0 (unlimited) duration=2 h (2 h)
Dump on exit: false
Path: /tmp/gc-baseline_12345_2026-04-20_101530.jfr
Read: Recording is active, has captured 48KB so far, max age 2h, writing to disk. Confirm maxage reflects the intended retention window. A duration of 0 (unlimited) is expected when only maxage is set — these are independent parameters.
[0.952s][info][gc,start ] GC(0) Pause Young (Normal) (G1 Evacuation Pause)
[0.956s][info][gc,cpu ] GC(0) User=0.01s Sys=0.00s Real=0.00s
Read each event: timestamp → log level → tag(s) → GC ID + phase → details. The Real= value in the [gc,cpu] line is the wall-clock pause duration.
2026-04-20T10:15:30.123+0000: 1.234: [GC (Allocation Failure) ...] 196608K->139264K(524288K), 0.0234321 secs
Read: datestamp → uptime → GC type → total heap Before->After(Max) → pause seconds. The value before secs is the wall-clock pause duration.
jfr print --summary Outputjfr print --summary /path/to/recording.jfr
Shows event counts grouped by type. Look for:
jdk.GarbageCollection count — how many GC cycles occurredjdk.GCPhasePause — individual pause phases; check max durationjdk.ObjectAllocationInNewTLAB count — very high counts indicate allocation pressure| If the blocker is... | Read... |
|---|---|
| confirming which collectors exist on an LTS baseline, comparing defaults, or deciding between G1/ZGC/Shenandoah | ./references/collector-baselines.md |
| configuring GC logging for next deploy, interpreting GC log output, translating JDK 8 legacy flags to unified logging | ./references/gc-logging.md |
| analyzing JFR recordings for GC events, querying specific events, interpreting event output | ./references/jfr-gc-events.md |
| Anti-pattern | Why it fails | Correct move |
|---|---|---|
| switching collectors before reading evidence | collector change becomes guesswork | confirm the collector and read logs/JFR first |
| discussing ZGC or Shenandoah without the LTS baseline | availability and maturity assumptions can be wrong | anchor the conversation in the deployed Java LTS first |
| recommending Shenandoah without checking the vendor build | some distributions do not ship it at all | confirm the actual JDK vendor or distribution before suggesting it |
recommending -Xlog:gc for a JDK 8 runtime | unified logging is not available there | use legacy GC logging flags on JDK 8 and earlier |
| recommending CMS on a modern runtime | CMS was removed and old guidance may no longer apply | treat CMS as a legacy JDK 8-era option only |
| tuning flags before proving GC is the bottleneck | the real issue may be allocation rate, leaks, or non-GC runtime behavior | verify that the symptom is genuinely GC-driven |
| reading GC symptoms without stating the workload goal | pause, throughput, and footprint recommendations diverge | name the dominant operational goal explicitly |