RadhaPublication
HomeInterviewLearning PathsRole TracksMockBooksPricing
Login / Sign UpLog in

© 2026 Radha Publication. All rights reserved.

Shop on AmazonDonateContactPrivacyTerms
Java questions
TrickyJava#157 min readJul 11, 2026

Minor GC vs Major GC vs Full GC.

practice
learning
garbage-collection
jvm
Practice modeTest yourself instead of reading straight through

Think of the heap like a busy office: quick desk cleanups are cheap, but shutting the whole office to mop every room is expensive — that is why interviewers love this question.

Question: Minor GC vs Major GC vs Full GC.

Answer: Minor GC usually means collecting the young generation, where most short-lived objects die. Major GC usually means collecting the old generation, where long-lived objects live, but the term is informal and collector-dependent. Full GC means collecting the whole heap, and often class metadata too; it is usually the longest stop-the-world pause.

Interview-Ready Answer: In Java, Minor GC typically cleans the young generation, Major GC targets the old generation, and Full GC collects the whole heap and often metaspace too. The key detail is that these names are collector-dependent, but in general Minor GC is short and frequent, Major GC is longer, and Full GC is the most expensive because it usually stops the world.

🧠 Memory Map
Memory map — visual summary of this topic

Detailed Explanation: Java objects are usually created in the young generation. The young gen is made of Eden, where new objects land, and Survivor spaces, which hold objects that survive a collection. A root is a starting point for reachability, such as a stack local, static field, or JNI reference. If an object is reachable from a root, the JVM must keep it. The generational idea is simple: most objects die young, so the JVM spends effort only where the garbage is most likely to be.

How it works under the hood

  1. Most new objects are born in Eden.
  2. When Eden fills, the JVM performs a Minor GC, also called a Young GC: it stops application threads, scans young objects, and copies the live ones into Survivor space or promotes them to old gen if they are old enough or Survivor is full.
  3. Because most objects die quickly, this collection is usually fast. The collector does not need to scan the whole heap, only the young area.
  4. As objects keep surviving, they age. Aging means surviving one more young collection; after enough age, they are promoted to old gen. Promotion is simply moving an object to the long-lived area.
  5. When the old generation gets crowded, the JVM may do a Major GC or Old GC. In classic collectors this focuses on old objects; in modern collectors like G1, you may instead see mixed collections, where young and some old regions are collected together.
  6. If the JVM cannot make progress, needs compaction, or must reclaim class metadata, it can trigger a Full GC. Full GC is usually stop-the-world, meaning application threads pause while the JVM works.

Comparison table

GC typeScopeTypical pauseCommon note
Minor GCYoung genMillisecondsFrequent
Major GCOld genLongerCollector dependent
Full GCWhole heapLongestOften compacts

Why this matters in interviews

These terms are really about pause cost and object lifetime. Minor GCs are frequent and usually cheap; Major GCs are less frequent and more expensive; Full GCs are the most painful because they usually stop all application threads and may compact memory. Compaction means moving objects together so free space becomes contiguous again.

Performance intuition: think of Minor GC as proportional to the live objects in the young gen, Major GC as proportional to the live old objects, and Full GC as proportional to the total live heap plus extra bookkeeping. In real systems, Minor GC pauses are often a few milliseconds to a few tens of milliseconds, while Full GC can easily become hundreds of milliseconds or even seconds on large heaps.

Version note: Since JDK 9, G1 has been the default HotSpot collector, and the old labels are less clean there. ZGC and Shenandoah are concurrent, low-pause collectors, so the classic Minor/Major/Full vocabulary matters less than understanding what work happens and whether the pause is stop-the-world.

Important gotcha: Major GC is not a formal Java language term. Different collectors and logs may use the words differently, so always read GC logs in the context of the collector you are using.

Real-World Story: Imagine a flash-sale checkout service. Most request objects, JSON parsing buffers, and temporary price calculations live for a few milliseconds, so the JVM mostly does Minor GCs. That is fine. Then a developer adds a static cache of request objects for debugging, and suddenly old gen starts filling with long-lived junk. Latency spikes, users see checkout timeouts, and logs start showing full GC pauses or allocation failures depending on the collector. The real bug is not Java itself; it is holding references too long, which prevents garbage collection and pushes the JVM into expensive old or full collections.

What the incident looks like: p95 latency jumps from 40 ms to 2 s, CPU may dip during long stop-the-world pauses, and error logs can mention full gc, evacuation failure, or promotion failure. That is the moment to inspect heap retention, not just add more heap blindly.

Java
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class GcTypesDemo {
    private static final class Payload {
        // A small payload makes the object real enough to show reachability.
        private final byte[] data = new byte[64 * 1024];
    }

    public static void main(String[] args) throws Exception {
        printCollectors("Startup");
        printHeap("Startup");

        ReferenceQueue<Payload> queue = new ReferenceQueue<>();
        Payload strong = new Payload();
        WeakReference<Payload> weak = new WeakReference<>(strong, queue);

        System.out.println();
        System.out.println("Created one object with a strong reference and one weak reference.");
        System.out.println("Before nulling the strong reference, weak.get() != null = " + (weak.get() != null));

        strong = null; // now the object is only weakly reachable

        // Create short-lived garbage to encourage a young collection.
        List<byte[]> garbage = new ArrayList<>();
        for (int i = 0; i < 2000; i++) {
            garbage.add(new byte[1024]);
        }
        garbage = null; // short-lived objects become GC fodder

        System.gc(); // a hint, not a guarantee

        boolean collected = waitForCollection(weak, queue, 5);
        System.out.println("Collected within 5 seconds? " + collected);
        System.out.println("weak.get() == null = " + (weak.get() == null));

        if (!collected) {
            System.out.println("Edge case: the JVM may delay or ignore System.gc(); GC is not guaranteed on demand.");
        }

        printHeap("End");
        printCollectors("End");
    }

    private static boolean waitForCollection(WeakReference<Payload> weak, ReferenceQueue<Payload> queue, int seconds) throws InterruptedException {
        long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(seconds);
        while (System.nanoTime() < deadline) {
            if (weak.get() == null || queue.poll() != null) {
                return true;
            }
            // Create tiny temporary pressure so the JVM has a reason to run a young collection.
            byte[] pressure = new byte[256 * 1024];
            if (pressure.length == 0) {
                System.out.println("Impossible");
            }
            Thread.sleep(25);
        }
        return weak.get() == null;
    }

    private static void printHeap(String label) {
        MemoryMXBean memory = ManagementFactory.getMemoryMXBean();
        MemoryUsage heap = memory.getHeapMemoryUsage();
        long usedMb = heap.getUsed() / (1024 * 1024);
        long committedMb = heap.getCommitted() / (1024 * 1024);
        long maxMb = heap.getMax() <= 0 ? -1 : heap.getMax() / (1024 * 1024);
        System.out.println(label + " heap usage: used=" + usedMb + " MB, committed=" + committedMb + " MB, max=" + maxMb + " MB");
    }

    private static void printCollectors(String label) {
        System.out.println(label + " GC collectors:");
        for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) {
            System.out.println("  - " + bean.getName() + " | collections=" + bean.getCollectionCount() + " | time=" + bean.getCollectionTime() + " ms");
        }
    }
}

Follow-up & Tricky Questions:

  • How do G1, ZGC, and Shenandoah change these terms? G1 still has young, mixed, and full pauses, but the old Minor/Major language is less clean. ZGC and Shenandoah try to do most work concurrently, so pauses are typically much shorter and the classic labels matter less.
  • What usually triggers a Full GC? Allocation failure, metaspace pressure, explicit calls such as System.gc(), or cases where the JVM needs compaction or cannot continue with the current collector state.
  • Why is Full GC such a latency problem? It usually stops application threads while scanning and possibly moving a large amount of live data, so it creates the longest pauses and the biggest tail-latency spikes.
  • How do you reduce Full GCs? Fix retention bugs, size the heap sanely, reduce allocation churn, and pick a collector that matches the latency goal. Often the fastest win is removing accidental long-lived references.
  • What is the difference between young generation and survivor space? Young generation is the whole area for new objects; survivor spaces are the smaller buffers inside it that hold objects which survived the last Minor GC.
  • Is Major GC always the same as Full GC? No. Major GC usually means old-gen collection, while Full GC means the whole heap, and often metaspace, is collected.
  • Does System.gc() guarantee a Full GC? No. It is only a request, and the JVM may ignore or delay it; explicit GC can even be disabled with -XX:+DisableExplicitGC.
  • Does every collector use the words Minor, Major, and Full the same way? No. That is the big gotcha: the names are useful shorthand, but the exact meaning depends on the collector and the GC log format.

Tricky gotchas:

  • Is Minor GC just a smaller Full GC? No. Minor GC usually touches only young objects, which is why it is much faster.
  • Does a Full GC fix memory leaks? No. If your code still holds references, the objects are still reachable and cannot be reclaimed.
  • Can an object jump directly to old gen? Yes, in some collectors or object sizes, promotion rules and allocation paths can bypass the normal young-to-old path.

Common Mistakes:

  • Calling Minor GC, Major GC, and Full GC exact JVM-spec terms. Correction: they are practical labels, and Major GC is especially collector-dependent.
  • Assuming Full GC is just a bigger Minor GC. Correction: Full GC usually touches the whole heap and may compact memory, so it is far more expensive.
  • Thinking more heap always solves GC pauses. Correction: a larger heap can actually make Full GCs slower if you keep too much live data.
  • Ignoring object retention. Correction: most GC pain comes from holding references too long, not from the act of allocation itself.

Memory Hook: Nursery, storage room, whole warehouse shutdown: Minor GC empties the nursery, Major GC cleans the storage room, and Full GC closes the whole building and cleans everything.

Cheat Sheet:

  • Minor GC = young generation.
  • Major GC = old generation, but the term is informal.
  • Full GC = whole heap, often metaspace too.
  • Minor is usually short; Full is usually the longest pause.
  • GC cost depends more on live data than on total allocated memory.
  • Since JDK 9, G1 is the default HotSpot collector, so log wording can vary.

Practice Tasks:

  • Run the provided program once with the default JVM, then again with a smaller heap such as -Xmx64m, and observe how often GC work happens.
  • Add a static list that keeps every Payload alive and see how the weak-reference behavior changes.
  • Open GC logs for your JVM and map the messages to young, old, and full collections.
Previous
Back to Questions←→to navigate
Next

Recommended Resources

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryUsage; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; public class GcTypesDemo { private static final class Payload { // A small payload makes the object real enough to show reachability. private final byte[] data = new byte[64 * 1024]; } public static void main(String[] args) throws Exception { printCollectors("Startup"); printHeap("Startup"); ReferenceQueue<Payload> queue = new ReferenceQueue<>(); Payload strong = new Payload(); WeakReference<Payload> weak = new WeakReference<>(strong, queue); System.out.println(); System.out.println("Created one object with a strong reference and one weak reference."); System.out.println("Before nulling the strong reference, weak.get() != null = " + (weak.get() != null)); strong = null; // now the object is only weakly reachable // Create short-lived garbage to encourage a young collection. List<byte[]> garbage = new ArrayList<>(); for (int i = 0; i < 2000; i++) { garbage.add(new byte[1024]); } garbage = null; // short-lived objects become GC fodder System.gc(); // a hint, not a guarantee boolean collected = waitForCollection(weak, queue, 5); System.out.println("Collected within 5 seconds? " + collected); System.out.println("weak.get() == null = " + (weak.get() == null)); if (!collected) { System.out.println("Edge case: the JVM may delay or ignore System.gc(); GC is not guaranteed on demand."); } printHeap("End"); printCollectors("End"); } private static boolean waitForCollection(WeakReference<Payload> weak, ReferenceQueue<Payload> queue, int seconds) throws InterruptedException { long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(seconds); while (System.nanoTime() < deadline) { if (weak.get() == null || queue.poll() != null) { return true; } // Create tiny temporary pressure so the JVM has a reason to run a young collection. byte[] pressure = new byte[256 * 1024]; if (pressure.length == 0) { System.out.println("Impossible"); } Thread.sleep(25); } return weak.get() == null; } private static void printHeap(String label) { MemoryMXBean memory = ManagementFactory.getMemoryMXBean(); MemoryUsage heap = memory.getHeapMemoryUsage(); long usedMb = heap.getUsed() / (1024 * 1024); long committedMb = heap.getCommitted() / (1024 * 1024); long maxMb = heap.getMax() <= 0 ? -1 : heap.getMax() / (1024 * 1024); System.out.println(label + " heap usage: used=" + usedMb + " MB, committed=" + committedMb + " MB, max=" + maxMb + " MB"); } private static void printCollectors(String label) { System.out.println(label + " GC collectors:"); for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) { System.out.println(" - " + bean.getName() + " | collections=" + bean.getCollectionCount() + " | time=" + bean.getCollectionTime() + " ms"); } } }