Skip to content

GCViewer: Offline GC Log Analysis and Tuning ​

GCViewer is one of the oldest GC log visualizers in the JVM world: a single jar, pure Java desktop app, LGPL licensed. Double-click it and you get the heap sawtooth plus dozens of statistics — the log never leaves your machine.

EasyGC reads the same log, just in a different shape: EasyGC runs in the browser (no install, leak detection and a recommendation engine), GCViewer fits air-gapped networks where logs cannot be shared, batch/CI runs that need structured output, and digging through raw GC events one by one. Their conclusions can be cross-checked against each other.

1. Install and launch ​

Current release: 1.37 (published 2026-05-25).

WhereLink
Executable jarhttps://sourceforge.net/projects/gcviewer/files/gcviewer-1.37.jar/download
macOS app bundlegcviewer-1.37-dist-mac.zip (same SourceForge folder)
Maven Centralcom.github.chewiebug:gcviewer:1.37

First gotcha

The GitHub release page has no attached binaries — only links to SourceForge. If you cannot find the jar, that is why.

You need a Java 8+ VM to run it:

bash
java -jar gcviewer-1.37.jar            # start the GUI
java -jar gcviewer-1.37.jar gc.log     # start the GUI and load this log

Build from source (Maven 3, output lands in target/):

bash
git clone https://github.com/chewiebug/GCViewer.git
cd GCViewer && mvn clean install

Reuse its parser in your own tooling via Maven Central:

xml
<dependency>
  <groupId>com.github.chewiebug</groupId>
  <artifactId>gcviewer</artifactId>
  <version>1.37</version>
</dependency>

2. Produce a log GCViewer can parse ​

How much you see depends entirely on the flags you turned on.

JDKRecommended flagsNote
Java 9+ (unified logging)-Xlog:gc=info:file="gc.log":tags,uptime,levelthe documented minimum
Java 9+ (recommended)-Xlog:gc*:file=gc.log:tags,time,uptime,levelmost complete — phases and concurrent cycles included
Java 9+ (maximum)-Xlog:gc*=trace:file="gc.log":tags,time,uptime,levelextra tags are ignored
Oracle JDK 6 / 7 / 8-Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStampsthe best-practice combination
Sun JDK 1.4 / 1.5-Xloggc:gc.log -XX:+PrintGCDetails
Sun JDK 1.2.2 / 1.3.1-verbose:gcno timestamps → no Total Time / Throughput
IBM JDK 1.2.2 – 1.4.2-verbose:gcmake sure events are not wrapped
HP-UX JDK 1.2 – 1.4-Xverbosegc
BEA JRockit 1.4.2 – 1.6-verbose:memory -Xverbose:gcpause,gcreport

Two extra flags decide how many metrics you get:

  • -XX:+PrintGCDetails — without it you lose the young/tenured areas, Promotion and InitiatingOccFraction. On Java 9+, -Xlog:gc* already covers this.
  • -XX:+PrintGCApplicationStoppedTime — required for VM operations overhead, the safepoint time that is not GC (biased-lock revocation, thread dumps, class redefinition).

Version difference

In 1.36 and earlier, extra decorations in unified logging break parsing; 1.37 supports all of them. If an older build refuses your -Xlog file, narrow the decorations to tags,time,uptime,level.

3. Reading the GUI ​

Opening a log ​

File → Open File for one file; File → Open Series for rotated logs (gc.log.0…gc.log.N read as one continuous log); File → Open URL accepts a URL directly.

Two handy switches during a load test:

  • File → Watch — reloads automatically whenever the file changes;
  • View → show datestamps — toggles between seconds since log start and absolute timestamps.

The chart ​

LegendMeaningWhat it tells you
Total Heap (red line)allocated heap sizewhether the heap keeps growing
Used Heap (blue line)actual occupancysawtooth = healthy; rising valley floors = leak
Young / Tenured Generation (orange / magenta areas)per-generation size (needs PrintGCDetails)how far old gen is from its ceiling
Used young / used tenured heaplive data per generationwhere survivors concentrate
Full GC Lines (black)every Full GCdensity = Full GC frequency
Inc GC Lines (cyan)every incremental GC
GC Times Line (green)pause trendpauses getting longer or steady?
GC Times Rectanglesper-pause duration, color-coded for Full GC / initial-mark / remark / VM operation / normal / incremental GCwhich event type your long pauses come from
Initial mark level (yellow)heap level when the concurrent cycle starts (CMS / G1)is it triggered too late?
Concurrent collections (cyan begin / pink end)concurrent cyclesdo cycles finish before the next one starts?

Every layer can be toggled in the View menu; 1.37 adds scroll-wheel zoom; right-clicking the time ruler lets you set the absolute start time (auto-filled when the log carries PrintGCDateStamps).

Event details ​

  • Gc pauses — stop-the-world pauses that are not Full GCs;
  • Full gc pauses — collections spanning generations or named full gc;
  • VM operations overhead (safepoint pauses) — safepoint time beyond GC (needs PrintGCApplicationStoppedTime);
  • Concurrent GCs — concurrent cycle information.

Check the Parser tab first

The neighbouring Parser tab lists warnings from the parsing run. Any error means lines were dropped and the statistics are incomplete — fix that before trusting a number.

The data panel (View → Data Panel) ​

Summary

MetricMeaningWhat to look for
Footprintmaximum heap used in the windowthe minimum memory this box needs
Total Timeduration covered by the loga couple of minutes is not a baseline
Throughputapp time share = 1 − accumulated pauses / total time> 95% healthy, < 90% tune it
Accumulated pauses / GC / full GCcumulative pause per categorythe Full GC row should be near zero
Freed Memory / Freed by GC / Freed by full GCbytes reclaimed
Freed Mem/Minbytes reclaimed per minutea rough allocation rate
GC Performance / Full GC Performancebytes reclaimed per unit of timecollection efficiency
Slope GC / Slope full GCgrowth slope of heap after GCpersistently positive = leak signal
Avg rel inc after GC / after FGCrelative heap increase after each GCthe same signal from another angle

Memory

MetricMeaning
Total / Tenured / Young / Perm heap (usage / alloc. max)usage vs. allocation ceiling per generation
Avg / Max heap after conc GCheap occupancy after concurrent cycles (the floor for Footprint)
Avg / Max heap after full GCheap occupancy after Full GC — close to Footprint means you are out of capacity
Avg / Max tenured after conc GCold-gen level
Avg / Total promotionpromotion volume (needs PrintGCDetails) — how much survives each Young GC
InitiatingOccFraction (avg / max)old-gen occupancy when the concurrent cycle fires (the basis for -XX:CMSInitiatingOccupancyFraction)

Pause

MetricMeaning
Number of pauses / gc pauses / full gc pausespause counts
Accumulated / Avg / Min / Max Pausecumulative, average, minimum and maximum pause
Avg pause interval / Min / max pause intervalinterval between pauses — a better frequency signal than raw counts
The same set for full gc pausesFull GC in isolation

How to read them

Max Pause maps straight onto your SLO ("P99 < 200 ms"); an unusually small Avg pause interval means GC thrashing; Total pause / Total Time is the mirror image of throughput.

4. Command line: batch and CI ​

No argument (or only a log) starts the GUI — two or more arguments switch to command-line mode:

bash
# report only (defaults to -t SUMMARY)
java -jar gcviewer-1.37.jar gc.log summary.csv

# report + heap chart
java -jar gcviewer-1.37.jar gc.log summary.csv chart.png

# pick the export type
java -jar gcviewer-1.37.jar gc.log summary.csv chart.png -t CSV_TS

# rotated logs: join them with semicolons
java -jar gcviewer-1.37.jar "gc.log.0;gc.log.1;gc.log.2;gc.log.current" summary.csv
PositionArgumentRequiredMeaning
1<gc-log-file|url>yesone log, a semicolon-separated rotation series, or a URL
2<export.csv>yesreport output path
3[chart.png]noalso render the heap chart
—-t <type>noexport type, defaults to SUMMARY

Choosing an export type:

TypeOutputBest for
SUMMARYfull summary (everything in the GUI data panel)human review, archiving
CSVcomma separated, aggregatedspreadsheets
CSV_TSone row per GC event with a unix timestamppipelines, trend charts, diffing two tuning runs
PLAINplain data (HPjmeter compatible)feeding HPjmeter
SIMPLE<event name> <seconds since start> <pause>feeding gchisto

Exit codes (straight from the source constants), useful in CI:

CodeMeaning
0success
-1export failed
-2argument parsing failed (usage is printed)
-3more than three positional arguments (usage is printed)

Batch-processing a night of logs:

bash
for f in /var/log/app/gc.log.*; do
  java -jar gcviewer-1.37.jar "$f" "${f}.csv" "${f}.png" -t CSV_TS || echo "FAILED: $f"
done

Shell escaping

The semicolon separates log files — quote it, or your shell treats it as the end of the command.

5. Four tuning paths ​

Walking these four paths beats staring at every number.

1. Low throughput (Throughput < 95%) ​

Check Throughput and Accumulated pauses in Summary → open the Pause tab and look at Number of gc pauses and Avg pause interval.

  • Many pauses, short intervals, each one short → young gen too small: raise -Xmn or lower -XX:NewRatio;
  • A few very long ones → that is path 3.

Verify: throughput up, Avg pause interval up (GC gets rarer).

2. Frequent Full GC / old gen saturated ​

Look at the density of Full GC Lines → compare Max heap after full GC with Footprint in the Memory tab:

  • close together → out of capacity: raise -Xmx, review cache sizes and data loading;
  • far apart yet Full GCs keep coming → usually System.gc() or metaspace; check the reason in Event details;
  • the blue Used Heap line has rising valley floors and Slope GC is positive → memory leak: GCViewer can go no further, switch to jmap + MAT (see JVM Diagnostic Tools).

3. A single pause breaks the SLO ​

Check Max Pause → find the matching GC Times Rectangles color to identify the event type:

  • black (Full GC) → back to path 2;
  • remark / initial-mark (CMS) → the concurrent cycle starts too late: lower -XX:CMSInitiatingOccupancyFraction, or move to G1;
  • G1 with a long tail → set -XX:MaxGCPauseMillis, look for humongous allocations (raise -XX:G1HeapRegionSize);
  • extremely latency-sensitive → ZGC / Shenandoah (1.37 improved parsing for both).

4. Promotion pressure: a quietly fattening old gen ​

Watch Avg / Total promotion and Max tenured after conc GC:

  • high promotion plus a rising old gen → the young gen is too small (premature promotion) or Survivor overflows: enlarge the young gen, tune -XX:SurvivorRatio, raise -XX:MaxTenuringThreshold;
  • high promotion but a stable old gen → normal, just keep watching.

5. The easy misdiagnosis ​

When VM operations overhead clearly exceeds the GC pauses, this is not a GC problem — it is other safepoint work (biased-lock revocation, thread dumps, class redefinition). Heap flags will not help.

6. GCViewer or EasyGC? ​

GCViewerEasyGC
Shapedesktop jar, needs Java 8+web page, opens instantly
Where the log goeslocallocal (parsed in the browser, never uploaded)
Chartsheap / generations / pause rectangles, zoom, layer togglesheap usage sawtooth, pause distribution, GC cause donut
Event detailsindividual events + parser warningsfull event table
Leak detectionSlope / chart valley floors (manual)linear regression with an automatic verdict
Tuning advicenone15 rules with four severity levels
Batch / CIcommand line, scriptablemanual upload
Structured exportCSV / CSV_TS / PLAIN / SIMPLE / PNGPDF / Word / JSON
Intranet deploymentcopy the jarstatic site, also deployable internally

A good combination: EasyGC first for the verdict and recommendations, GCViewer to double-check when you need offline work, batch runs or raw events. Two tools, one log, far more confidence.

7. Known traps ​

  • Download location: the jar lives on SourceForge, not in GitHub release assets.
  • Older versions choking on unified logs: extra decorations break parsing before 1.37 — upgrade or trim them.
  • CMS / G1 mixing concurrent events with STW pauses: the README states the parser sometimes cannot recover — a known limitation.
  • Huge logs: GCViewer is a Java program too — run it with java -Xmx2g -jar gcviewer.jar for several-hundred-MB logs.
  • Rotated logs: semicolons on the command line (quoted), File → Open Series in the GUI.
  • Missing metrics: no PrintGCDetails → no promotion, no generation areas; no PrintGCApplicationStoppedTime → no safepoint overhead.
  • Never ignore parser warnings: with unparsed lines, every statistic describes only part of the log.