Minor GC, major GC, full GC: you see these words everywhere, but not always with the meaning the official documentation gives them.
Most objects die young. So the JVM keeps new objects in the young generation and long-lived ones in the old generation. A GC that only cleans the young generation is fast, and it runs often.
Why generations?
Oracle’s official guide to the garbage collector starts from a simple fact: most objects live for a very short time. This is called the generational hypothesis. An object created for a request, a loop or a calculation is useless almost right away.
If the JVM keeps new objects apart from long-lived ones, it can often clean the small area of new objects, where almost everything is dead, without scanning the whole heap every time.
How an object moves: eden, survivor, old
The young generation has three parts: eden and two survivor spaces.
- An object is almost always created in eden.
- When the young generation is full, the GC cleans it. Objects that are still alive are copied to a survivor space, and eden is empty again.
- At the next GC, live objects move from one survivor space to the other. One of the two is always empty.
- After a number of copies, or when the survivor spaces are full, the object is copied to the old generation. We say it is promoted.
You set the maximum number of copies with -XX:MaxTenuringThreshold.
The default is 15.
Minor, major, full: what the docs really say
In Oracle’s guide:
- a minor collection only cleans the young generation;
- a major collection cleans the whole heap, when the old generation is full. It usually takes much longer;
- the term full GC shows up, for example, for a complete clean-up
asked for by
System.gc().
Many articles say something else: “major GC = old generation only, full GC = the whole heap”. That is not how the official guide defines it.
And in the logs, the words depend on the GC. G1, the default GC, never writes “minor” or “major”:
[0.238s] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 35M->3M(64M) 2.406ms
[0.287s] GC(34) Pause Young (Mixed) (G1 Evacuation Pause) 60M->35M(64M) 0.215ms
[0.335s] GC(82) Pause Full (System.gc()) 53M->27M(64M) 3.010ms
A Young (Normal) pause cleans the young generation. A Young
(Mixed) pause cleans the young generation plus part of the old one.
ZGC, on the other hand, does write Minor Collection and Major Collection:
[0.246s] GC(3) Minor Collection (Allocation Rate) 52M(81%)->52M(81%) 0.008s
[0.503s] GC(152) Major Collection (System.gc()) 38M(59%)->30M(47%) 0.009s
So when you read a GC log, first check which GC wrote it.
When does G1 run a full GC?
With G1, a full GC is a last resort: it compacts the whole heap, and the docs warn that it can be very slow. It mostly happens in two cases:
- the heap is too full: the application creates objects faster than G1 can free space in the old generation;
- someone asks for it: a call to
System.gc(), from the application or from a tool.
A full GC now and then is not a problem. Full GCs that keep coming back are a warning sign: the heap is too small, or there is a memory leak.
What has changed
Since Java 9, G1 is the default GC. It no longer splits the heap into two big blocks: it divides it into regions of the same size (4 MB on a machine with 32 GB of memory). The young and old generations are sets of regions, and these regions are not always next to each other in memory.
| What you still read online | What is true today | Since |
|---|---|---|
| The heap is split into two fixed blocks, young and old | With G1, the heap is made of regions of the same size; the young and old generations are sets of regions | Java 9 (G1 by default) |
| ZGC has no generations | ZGC is generational: an option in Java 21, the default in Java 23, the only mode since Java 24 (-XX:+ZGenerational is ignored, with a warning) | Java 21 to 24 |
| Major GC = cleaning the old generation | In Oracle’s guide, a major collection cleans the whole heap | — |
You tune the young generation with -Xmn or -XX:NewRatio | With G1, the docs advise against it: it mostly turns off pause-time control | — |
Sources: Oracle’s garbage collection guide (JDK 25), JEP 248 (G1 by default), JEP 439, JEP 474 and JEP 490 (generational ZGC).
The options in short
-Xmn: the size of the young generation (initial and maximum);-XX:NewRatio: the size of the old generation compared to the young one (2 by default: the old generation is twice as big);-XX:SurvivorRatio: the size of eden compared to one survivor space (8 by default);-XX:MaxTenuringThreshold: the number of copies before promotion (15 by default).
With G1, it is better not to fix the size of the young generation: G1 adjusts it by itself to meet its pause-time goal.
Summary
Objects are born in eden, survive a few GCs in the survivor spaces, and the long-lived ones end up in the old generation. “Minor” means the young generation; “major”, in Oracle’s docs, means the whole heap. With G1, you rather see young, mixed and full pauses, and the heap is made of regions.