Turning on the GC log is easy. Reading it is harder: one G1 line holds about ten pieces of information, and the words change from one Java version to the next.

This post reads real lines, written by three small programs of the public repository java-gc-lab: a normal load, very big objects, and a heap that is almost full.

Turn on the log

java -Xlog:gc*:file=gc.log:time,uptime,level,tags -jar app.jar
  • gc*: all the GC messages (gc alone gives one line per GC);
  • file=gc.log: in a file, with rotation built in (5 files of about 20 MB by default);
  • time,uptime,level,tags: the “decorations” at the start of each line: the date and time, the time since the JVM started, the level and the tags. If you set nothing, the JVM uses uptime, level and tags.

A pause line, with these decorations:

[2026-10-22T23:23:46.842+0200][0.356s][info][gc          ] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 117M->24M(256M) 1.660ms

The exact time (time) helps to link a pause to an event of the application, for example a slow request in another log.

One line, word by word

One G1 log line on Java 25, cut into five parts: GC(1), the number of the GC; Pause Young (Normal), the type of pause; (G1 Evacuation Pause), the cause; 117M->23M(256M), the heap before and after the GC and its size; 5.790ms, the length of the pauseOne G1 log lineJava 25, -Xlog:gcGC(1)GC numberPause Young (Normal)type of pause(G1 Evacuation Pause)cause of the GC117M->23M(256M)heap before -> after (size)5.790mspause time
PartMeaning
[0.469s]the time since the JVM started (uptime)
GC(1)the number of the GC: all the lines of one GC have the same one
Pause Young (Normal)the type of pause: here a normal young pause. You also see Concurrent Start, Prepare Mixed, Mixed, Remark, Cleanup and Full
(G1 Evacuation Pause)the cause of the GC, here the usual one. Other causes: G1 Humongous Allocation, System.gc(), G1 Compaction Pause (full GC)
117M->23M(256M)the heap used before the GC, after the GC, and the heap size
5.790msthe length of the pause: the application is stopped during this time

The words young, mixed and full are explained in the post about generations.

The 4 numbers to follow

Here are the start and the end of the log of NormalLoad.java, an application that keeps about 16 MB of data and creates short-lived objects all the time:

java -Xmx256m -Xlog:gc gc-logs/NormalLoad.java
[0.341s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 47M->18M(256M) 3.090ms
[0.469s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 117M->23M(256M) 5.790ms
[0.646s][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 149M->24M(256M) 4.129ms
…
[3.240s][info][gc] GC(15) Pause Young (Normal) (G1 Evacuation Pause) 171M->24M(256M) 0.635ms
[3.478s][info][gc] GC(16) Pause Young (Normal) (G1 Evacuation Pause) 171M->24M(256M) 12.874ms
  1. The GC rate: 17 GCs in a little more than 3 seconds, about 5 per second. It goes up when the application creates more objects, or when the heap is too small.
  2. The pause time: from 0.4 to 12.9 ms here. Look at the longest ones first: they are the ones that users feel.
  3. The heap after the GC: 23 to 24 MB, stable. It is roughly the size of the live data. If it goes up from one GC to the next, it is a leak (see the post about OutOfMemoryError).
  4. The allocation rate: the heap before a GC, minus the heap after the previous GC, divided by the time between them. Between GC(0) and GC(1): (117 − 18) MB in 0.128 s, so about 770 MB/s. No line gives it directly: you have to compute it.

Warning signs

Humongous objects

java -Xmx256m -Xlog:gc gc-logs/Humongous.java
[0.306s][info][gc] GC(0) Pause Young (Concurrent Start) (G1 Humongous Allocation) 146M->9M(256M) 1.583ms

For G1, a “humongous” object is at least half a region. It does not go through eden: it gets its own regions, directly in the old generation. Here the program creates 2 MB arrays with 1 MB regions (Heap Region Size: 1M at startup with -Xlog:gc*), and the allocation itself starts the GC. Many G1 Humongous Allocation lines point to big arrays or big buffers created often.

An evacuation failure, then Pause Full

java -Xmx64m -Xlog:gc gc-logs/NearTheLimit.java
[0.295s][info][gc] GC(5) Pause Young (Prepare Mixed) (G1 Evacuation Pause) (Evacuation Failure: Allocation) 61M->47M(64M) 0.250ms
[1.348s][info][gc] GC(175) Pause Full (G1 Compaction Pause) 62M->45M(64M) 5.691ms

The program keeps about 30 MB of data in a 64 MB heap. G1 no longer finds enough room to copy the live objects: this is an evacuation failure. The reason is Allocation (no room) or Pinned (an object held in place by native code). If a GC frees nothing at all, G1 starts a full GC, which compacts the whole heap and can be very slow.

Before Java 18: the evacuation failure was called to-space exhausted. On Java 11 and 17, it was a separate line (GC(7) To-space exhausted); on Java 8, the end of the pause line. Java 21 writes (Evacuation Failure), and Java 25 adds the reason.

A Pause Full line with G1, when it does not come from System.gc(), is always worth a look: the heap is too small for the live data, or there is a leak.

Pauses that get longer

If the last number of the lines goes up over the hours, with the same type of pause, the GC has more and more work: more live data to copy, or a heap that fills up. Follow it closely.

The tools

  • JFR (Java Flight Recorder), in the JDK: start it with -XX:StartFlightRecording, and read the recording with JDK Mission Control. It shows the GC pauses with the rest of the application. More details in the post about tools.
  • Third-party tools also read the log files and draw charts, for example GCeasy (an online service) or GCViewer (open source).

What has changed

What you still readWhat is true todaySince
-XX:+PrintGCDetails, -Xloggc:gc.log-Xlog:gc*, -Xlog:gc*:file=gc.log (the old options are deprecated or refused, see the JVM options)Java 9
to-space exhaustedEvacuation Failure, then Evacuation Failure: <reason>Java 18 (JDK-8269022), the reason on Java 25 in my tests
-XX:+PrintGCDateStamps to get the timethe time decoration of -XlogJava 9

Tested with Java 8 (Corretto 8u504), 11, 17, 21 and 25 (Temurin 11.0.32, 17.0.20, 21.0.9, 25.0.4), on macOS, with G1.

Sources: the documentation of the java command (JDK 25, -Xlog), the G1 chapter of Oracle’s GC guide (humongous objects, evacuation failure, full GC), JDK-8269022 (the new name of the evacuation failure).

Further reading