Chris Collins

Tools · infra

Java GC log analyser

Read a JVM GC log — unified -Xlog:gc* for G1, ZGC (generational or not), Shenandoah, Parallel and Serial, or a JDK 8 PrintGCDetails log — and get a pause timeline, heap before and after, allocation and promotion rates, pause percentiles, throughput, full GCs and failures flagged, and tuning hints in plain words.

Added

Works with

How to use it

GC logs are cheap to keep and tell you most of what you need about a JVM’s memory: how big the live data is, how fast the application allocates, and whether the collector keeps up. This page turns one into numbers and a picture.

How to use it

Paste the log or drop the file (a .gz is unpacked in the page). To get a good one:

# JDK 9 and later
-Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=5,filesize=20m
# JDK 8
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log

The page reads:

  • Unified logging (-Xlog) for G1, Parallel, Serial, ZGC (single-generation in JDK 17–21, generational in JDK 21+), and Shenandoah — with any decorators (uptime, time, uptimemillis, level, tags…) and with gc or gc*. The more tags, the more it can say: region counts, generation sizes, CPU times and ZGC’s pause phases come from gc+heap, gc+cpu and gc+phases.

  • JDK 8 logs from -XX:+PrintGCDetails or -verbose:gc for Parallel, Serial, CMS and G1 (including G1’s multi-line pauses).

What you get:

  • Summary — throughput (the share of time not paused), pause count, median, 95th, 99th percentile and longest pause, total time paused, allocation rate, promotion rate, the heap after full GCs (a good estimate of the live data), and counts of full GCs, evacuation failures (to-space exhausted), promotion failures, CMS concurrent mode failures, Shenandoah degenerated cycles and ZGC allocation stalls.

  • What it means — findings in plain words with the flags to try: heap too small or a leak, full GCs and why, System.gc() calls, metaspace-triggered GCs, humongous allocations and the region size that would avoid them, pauses waiting for CPU (real time far above user+sys, a sign of container CPU throttling), high system time, Serial GC chosen by ergonomics in a small container, very frequent young collections.

  • Timeline — heap before and after each collection against the heap size, concurrent cycles as bands, pauses as bars by kind, full GCs and failures marked. Zoom with the two sliders; hover or tap for the collection under the cursor.

  • Pauses by kind, a pause histogram, causes, concurrent cycle durations, and the longest pauses.

Example loads real logs I made for this page: a G1 service with a slow leak that ends in full GCs (JDK 21), a JDK 8 Parallel log, and generational ZGC with allocation stalls.

Plan container memory for this heap opens the JVM memory planner with the heap size, collector, JDK version and CPU count from the log. A thread dump pasted here is offered to the thread dump analyser.

How the numbers are worked out

  • Allocation rate: for each pair of collections, the heap before the second minus the heap after the first, summed and divided by the time. For concurrent collectors (ZGC, Shenandoah) the heap keeps growing during a cycle, so it is an approximation.

  • Promotion rate: growth of the old generation across young collections — old regions × region size for G1, the old generation’s size for Parallel and Serial, and young-freed minus heap-freed for JDK 8 logs.

  • Throughput: 1 − (sum of pauses ÷ the time the log covers). ZGC and Shenandoah also slow the application with allocation stalls and pacing, which are counted separately.

  • Percentiles are nearest-rank.

I checked the parser against logs I generated on JDK 8, 11, 17, 21 and 24 with every collector (G1, Parallel, Serial, CMS, ZGC both modes, Shenandoah) and against the formats in the OpenJDK documentation; the tests record the numbers.

Browser support

Any current browser. A log of a few hundred thousand lines takes a second or two.

Privacy

The log is read in this page and never uploaded or stored. Hand-offs to the other JVM tools go through this browser’s IndexedDB and are deleted as they are read.

Runs entirely in your browser; nothing you type leaves this page.