[{"data":1,"prerenderedAt":126},["ShallowReactive",2],{"qa-\u002Fjava\u002Fjvm-internals\u002Fgarbage-collection":3},{"page":4,"siblings":113,"blog":123},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":12,"path":20,"questions":21,"questionsCount":104,"related":105,"seo":106,"seoDescription":107,"stem":108,"subtopic":6,"topic":109,"topicSlug":110,"updated":111,"__hash__":112},"qa\u002Fjava\u002Fjvm-internals\u002Fgarbage-collection.md","Garbage Collection",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"hard","md","Java","java",{},true,"\u002Fjava\u002Fjvm-internals\u002Fgarbage-collection",[22,27,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100],{"id":23,"difficulty":24,"q":25,"a":26},"what-is-gc","easy","What is garbage collection in Java and why is it needed?","**Garbage collection (GC)** is the automatic process by which the JVM\nidentifies and reclaims heap memory occupied by objects that are no longer\nreachable from any **GC root** (live thread, static field, JNI reference).\nWithout GC, developers would have to `free()` objects manually — the source\nof dangling-pointer and double-free bugs in languages like C.\n\n```java\nvoid createAndForget() {\n    String s = new String(\"temporary\"); \u002F\u002F allocated on heap\n}   \u002F\u002F s goes out of scope → no reference remains → object is unreachable\n    \u002F\u002F GC will reclaim it on the next collection cycle\n```\n\n**Rule of thumb:** GC removes unreachable objects; it does NOT remove\nobjects that are still referenced even if you'll never use them again —\nthat's a memory leak.\n",{"id":28,"difficulty":29,"q":30,"a":31},"mark-and-sweep","medium","How does the mark-and-sweep GC algorithm work?","**Mark-and-sweep** runs in two phases:\n\n1. **Mark** — start from every GC root and trace all reachable objects,\n   setting a \"live\" bit on each one.\n2. **Sweep** — scan the entire heap; reclaim any object without the live\n   bit set; clear live bits for next cycle.\n\n```\nHeap before GC:   [A (live)] [B (dead)] [C (live)] [D (dead)]\nAfter mark:       [A ✓]      [B  ]      [C ✓]      [D  ]\nAfter sweep:      [A]        [free]      [C]         [free]\n```\n\nThe downside is **fragmentation** — freed slots are scattered, making large\nallocations hard. Modern collectors add a **compaction** phase to slide live\nobjects together and reset the allocation pointer.\n\n**Rule of thumb:** mark-and-sweep tells you *what* to collect; compaction\ntells you how to make the freed space *usable*.\n",{"id":33,"difficulty":29,"q":34,"a":35},"generational-gc","Why do modern JVMs use generational garbage collection?","The **weak generational hypothesis** — most objects die young — means that\ncollecting only the small, short-lived Young Generation most of the time is\nfar more efficient than scanning the whole heap every GC cycle.\n\n| GC type | Region swept | Frequency | Typical pause |\n|---|---|---|---|\n| **Minor GC** | Young Gen (Eden + Survivors) | frequent | ms |\n| **Major\u002FFull GC** | Entire heap | infrequent | tens of ms → seconds |\n\n```java\n\u002F\u002F Allocation pattern that plays nicely with generational GC:\nvoid processRequest() {\n    List\u003CItem> results = new ArrayList\u003C>(); \u002F\u002F short-lived; dies in Eden\n    for (Item i : fetch()) results.add(process(i));\n    return results;\n}   \u002F\u002F results unreachable after method returns → collected cheaply\n```\n\n**Rule of thumb:** keep temporary objects truly temporary — objects that\nescape into long-lived caches defeat generational GC and inflate Old Gen.\n",{"id":37,"difficulty":24,"q":38,"a":39},"serial-gc","What is the Serial GC and when is it appropriate?","The **Serial GC** (`-XX:+UseSerialGC`) uses a **single thread** for both\nMinor and Major GC. The entire JVM stops during collection\n(stop-the-world). It is the simplest collector and has the lowest overhead\nper collection, but pauses are longest when the heap is large.\n\n```bash\njava -XX:+UseSerialGC -Xmx256m MyApp\n```\n\nIt is appropriate for:\n- **Small heaps** (\u003C ~256 MB)\n- **Single-core machines** or environments with very few CPUs\n- **Embedded** or **batch** workloads where pause time is not a concern\n\n**Rule of thumb:** Serial GC is correct for tiny, single-threaded CLI\ntools; any latency-sensitive server should use a concurrent collector.\n",{"id":41,"difficulty":24,"q":42,"a":43},"parallel-gc","What is the Parallel GC and how does it differ from Serial?","The **Parallel GC** (also called Throughput Collector, `-XX:+UseParallelGC`,\ndefault before Java 9) uses **multiple threads** for both Minor and Full\nGC, cutting pause times compared to Serial. The application still stops\nduring GC, but the work is divided across cores.\n\n```bash\njava -XX:+UseParallelGC -XX:ParallelGCThreads=8 MyApp\n```\n\nIt maximises **throughput** (fraction of time not spent in GC) at the cost\nof longer individual pauses than concurrent collectors. A good fit for\n**batch jobs** that care more about total work done than per-request latency.\n\n**Rule of thumb:** Parallel GC = maximum throughput, accept the pauses;\nchoose a concurrent collector (G1, ZGC) when latency matters.\n",{"id":45,"difficulty":29,"q":46,"a":47},"cms-gc","What was the CMS GC and why was it removed?","**Concurrent Mark-Sweep (CMS)** (`-XX:+UseConcMarkSweepGC`) ran most of its\nwork **concurrently** with the application, greatly reducing Old Gen pause\ntimes. It had two short stop-the-world phases (initial mark, remark) and\nlong concurrent phases for marking and sweeping.\n\nIts flaws:\n- **No compaction** — memory fragmentation grew over time, eventually\n  causing a \"concurrent mode failure\" that triggered a full stop-the-world\n  compacting collection.\n- High CPU overhead from running GC threads alongside the app.\n- Deprecated in Java 9, **removed in Java 14**.\n\n```bash\n# No longer valid in Java 14+:\n# java -XX:+UseConcMarkSweepGC ...\n```\n\n**Rule of thumb:** CMS is a historical reference; in interviews, position\nG1 as its replacement and ZGC\u002FShenandoah as the next evolution.\n",{"id":49,"difficulty":29,"q":50,"a":51},"g1-gc","How does the G1 garbage collector work?","**G1 (Garbage First)** is the default GC since Java 9. It divides the heap\ninto equal-sized **regions** (1–32 MB each) instead of fixed Eden\u002FSurvivor\u002F\nOld Gen areas. Each region is labelled dynamically.\n\nG1 collects the regions with the most garbage first (hence \"Garbage First\"),\nproviding **predictable pause targets** via `-XX:MaxGCPauseMillis` (default\n200 ms). It runs most work concurrently and compacts incrementally.\n\n```bash\njava -XX:+UseG1GC -XX:MaxGCPauseMillis=100 MyApp\n```\n\nKey G1 concepts:\n- **Humongous regions** — objects > 50 % of region size get their own\n  contiguous region(s); avoid creating many large short-lived objects.\n- **Mixed GC** — after a concurrent marking cycle, G1 collects Young Gen\n  plus the most garbage-dense Old Gen regions together.\n\n**Rule of thumb:** G1 is the right default for most Java apps; tune\n`-XX:MaxGCPauseMillis` first before reaching for exotic flags.\n",{"id":53,"difficulty":14,"q":54,"a":55},"zgc","What makes ZGC different from G1 and when would you choose it?","**ZGC** (`-XX:+UseZGC`, production-ready since Java 15) is designed for\n**sub-millisecond pause times** regardless of heap size (tested up to\nterabytes). It achieves this with three techniques:\n\n1. **Colored pointers** — GC metadata is encoded into the unused upper bits\n   of 64-bit references, eliminating separate per-object GC state.\n2. **Load barriers** — every reference load (not just writes) triggers a\n   barrier that fixes up pointers during concurrent relocation, so the app\n   never stops while objects are moved.\n3. **Concurrent relocation** — objects are moved while the application runs,\n   unlike G1 which stops for evacuation.\n\n```bash\njava -XX:+UseZGC -Xmx16g MyLatencySensitiveApp\n```\n\nChoose ZGC when **tail latency** (p99\u002Fp999) matters more than throughput —\ne.g., real-time APIs, trading systems, or interactive services.\n\n**Rule of thumb:** ZGC trades some throughput for near-zero pause times;\nG1 is simpler and usually sufficient unless you're measuring tail latencies\nbelow 10 ms.\n",{"id":57,"difficulty":14,"q":58,"a":59},"shenandoah","What is Shenandoah GC and how does it compare to ZGC?","**Shenandoah** is a low-pause GC developed by Red Hat, available in\nOpenJDK since Java 12. Like ZGC it relocates objects **concurrently**, but\nuses **Brooks pointers** (an indirection word in the object header) rather\nthan colored pointer bits, making it compatible with 32-bit builds and\ncompressed oops more easily.\n\n```bash\njava -XX:+UseShenandoahGC MyApp\n```\n\n| Aspect | ZGC | Shenandoah |\n|---|---|---|\n| Pause target | \u003C 1 ms | \u003C 10 ms |\n| Concurrent relocation | yes | yes |\n| Mechanism | colored pointers + load barriers | Brooks pointer + barriers |\n| Vendor | Oracle\u002FOpenJDK | Red Hat\u002FOpenJDK |\n\nBoth are good low-pause choices; ZGC has slightly lower pause guarantees\nbut higher CPU overhead from load barriers on every pointer dereference.\n\n**Rule of thumb:** ZGC or Shenandoah for ultra-low latency; the choice\noften comes down to benchmark results in your specific workload.\n",{"id":61,"difficulty":29,"q":62,"a":63},"stop-the-world","What is a stop-the-world pause in GC and why can't it be fully avoided?","A **stop-the-world (STW)** pause is a moment when all application threads\nare suspended so the GC can operate on a consistent snapshot of the heap.\nEven concurrent collectors like G1, ZGC, and Shenandoah need short STW\nphases (initial mark, final remark\u002Frerooting) because GC and application\nthreads cannot safely move objects *and* update every reference atomically\nwithout a brief freeze.\n\n```\nTimeline:\n  [App threads running] → [STW: initial mark ~few ms] → [App + GC concurrent]\n                       → [STW: remark ~few ms] → [App threads running]\n```\n\nThe goal of modern collectors is to make STW pauses **short and\npredictable** rather than eliminating them entirely (which is impossible in\ncurrent JVM designs without hardware transactional memory).\n\n**Rule of thumb:** no JVM GC is truly \"pause-free\"; the question is how\n*long* and *frequent* the pauses are.\n",{"id":65,"difficulty":29,"q":66,"a":67},"gc-roots","What are GC roots and why are they the starting point for collection?","**GC roots** are objects that are guaranteed to be live — they are the\nseeds from which the collector traces all reachable objects. Any object not\nreachable from a GC root is dead and eligible for collection.\n\nCommon GC roots:\n- Local variables in active **stack frames**\n- **Static fields** of loaded classes\n- **JNI references** (held by native code)\n- References in **active threads**\n\n```java\nstatic Map\u003CInteger, Object> registry = new HashMap\u003C>();\n\u002F\u002F Anything stored in registry is reachable via a static field (GC root)\n\u002F\u002F and will NEVER be collected while the class is loaded\n```\n\n**Rule of thumb:** a Java memory leak is almost always an object still\nreachable from a GC root that you forgot to remove (e.g., an event listener,\na static cache entry, a ThreadLocal that was never cleared).\n",{"id":69,"difficulty":29,"q":70,"a":71},"finalize","What is finalization in Java and why is it discouraged?","If a class overrides `Object.finalize()`, the GC places unreachable\ninstances of that class on a **finalization queue** instead of immediately\nreclaiming them. A dedicated finalizer thread eventually calls `finalize()`,\nthen the object becomes reclaimable on the *next* GC cycle.\n\nProblems with finalization:\n- **Unpredictable timing** — you don't know when or if `finalize()` will run.\n- **Performance** — objects survive at least two extra GC cycles.\n- **Resurrection** — `finalize()` can store `this` somewhere, making the\n  object live again — a source of hard-to-find bugs.\n- **Deprecated in Java 9, scheduled for removal.**\n\n```java\n\u002F\u002F Preferred alternative: AutoCloseable + try-with-resources\nclass Resource implements AutoCloseable {\n    @Override public void close() { \u002F* release resource deterministically *\u002F }\n}\ntry (Resource r = new Resource()) { r.use(); }\n```\n\n**Rule of thumb:** never rely on `finalize()`; use `AutoCloseable` and\n`try-with-resources` for deterministic resource cleanup.\n",{"id":73,"difficulty":29,"q":74,"a":75},"gc-logs","How do you enable and read GC logs in Java 11+?","Java 9+ uses the **Unified Logging** framework for GC output:\n\n```bash\njava -Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=5,filesize=20m MyApp\n```\n\nA typical G1 Minor GC log line:\n```\n[2.345s][info][gc] GC(3) Pause Young (Normal) (G1 Evacuation Pause) 128M->64M(512M) 12.345ms\n```\n\nKey fields: pause type, heap before → after (heap capacity), pause duration.\n\nUseful selectors:\n- `gc` — one line per GC event\n- `gc*` — verbose, includes region details\n- `gc+heap=debug` — heap breakdown before\u002Fafter\n\n**Rule of thumb:** always enable GC logging in production with rolling\nfiles; you cannot replay GC history if you don't capture it, and it is the\nfirst thing you need when diagnosing a latency incident.\n",{"id":77,"difficulty":29,"q":78,"a":79},"gc-tuning-flags","What are the most important GC tuning flags and what do they control?","| Flag | Effect |\n|---|---|\n| `-XX:+UseG1GC` | Select G1 (default Java 9+) |\n| `-XX:MaxGCPauseMillis=N` | G1 pause target (soft goal, default 200 ms) |\n| `-XX:GCTimeRatio=N` | Ratio of GC time to app time (Parallel GC) |\n| `-XX:NewRatio=N` | Old:Young size ratio (e.g., 2 = 1\u002F3 Young) |\n| `-XX:SurvivorRatio=N` | Eden:Survivor ratio within Young Gen |\n| `-XX:MaxTenuringThreshold=N` | GC cycles before promotion to Old Gen |\n| `-XX:G1HeapRegionSize=N` | G1 region size (1–32 MB, power of 2) |\n| `-XX:G1NewSizePercent` \u002F `-XX:G1MaxNewSizePercent` | Young Gen size range for G1 |\n\n```bash\n# Common production starting point for G1:\njava -XX:+UseG1GC -Xms4g -Xmx4g -XX:MaxGCPauseMillis=100 \\\n     -XX:+HeapDumpOnOutOfMemoryError MyApp\n```\n\n**Rule of thumb:** measure first with GC logs before tuning; changing flags\nwithout data is guesswork.\n",{"id":81,"difficulty":29,"q":82,"a":83},"object-promotion","When and how does an object get promoted from Young Gen to Old Gen?","Each time an object survives a Minor GC it is copied to the other Survivor\nspace and its **age counter** (stored in the mark word, max 15 bits) is\nincremented. When the age reaches `-XX:MaxTenuringThreshold` (default 15),\nthe object is **promoted** to Old Gen on the next Minor GC.\n\nPremature promotion also occurs when:\n- A Survivor space is more than 50 % full during a Minor GC — the JVM\n  lowers the effective threshold dynamically (**adaptive tenuring**).\n- The object is **too large** for Survivor — it bypasses Young Gen entirely\n  and goes straight to Old Gen (or a humongous region in G1).\n\n```java\n\u002F\u002F Anti-pattern: large, long-lived cache that pollutes Old Gen quickly\nstatic Map\u003CString, byte[]> cache = new ConcurrentHashMap\u003C>();\nvoid put(String key) {\n    cache.put(key, new byte[1024 * 1024]); \u002F\u002F immediately humongous in G1\n}\n```\n\n**Rule of thumb:** if Old Gen grows steadily without a leak, your objects\nare living too long — reduce object scope or use a proper cache with eviction.\n",{"id":85,"difficulty":29,"q":86,"a":87},"reference-types-gc","How do soft, weak, and phantom references affect GC behaviour?","Java provides reference wrappers that allow the GC to collect the referent\nunder different urgency levels, enabling memory-sensitive caches:\n\n| Type | GC collects when | Main use |\n|---|---|---|\n| `StrongReference` (normal) | Never while reachable | Everything |\n| `SoftReference\u003CT>` | Before throwing OOM | Memory-sensitive caches |\n| `WeakReference\u003CT>` | Next GC, regardless of memory | Canonicalizing maps |\n| `PhantomReference\u003CT>` | After finalization | GC notification \u002F cleanup |\n\n```java\nCache\u003CK, V> cache = new ConcurrentHashMap\u003C>();\nSoftReference\u003Cbyte[]> ref = new SoftReference\u003C>(new byte[1024 * 1024]);\nbyte[] data = ref.get(); \u002F\u002F null if GC evicted it\n```\n\n`WeakHashMap` uses weak references as keys — entries disappear as soon as\nthe key has no strong referent elsewhere.\n\n**Rule of thumb:** prefer explicit cache eviction (size\u002Ftime bounds) over\n`SoftReference` caches — the GC's eviction timing is unpredictable.\n",{"id":89,"difficulty":14,"q":90,"a":91},"concurrent-mode-failure","What is a concurrent mode failure in G1 and how do you fix it?","A **concurrent mode failure** (also called **evacuation failure** in G1)\noccurs when the GC cannot complete its concurrent collection fast enough —\nOld Gen fills up while the concurrent marking cycle is still running. The\nJVM falls back to a **full stop-the-world collection** to avoid an OOM.\n\nIn G1 this appears in logs as:\n```\n[gc] GC(42) To-space exhausted\n[gc] GC(43) Pause Full (G1 Compaction Pause) 2048M->1200M(2048M) 8432.345ms\n```\n\nCommon causes and fixes:\n| Cause | Fix |\n|---|---|\n| Objects promoted to Old Gen faster than G1 marks them | Increase heap (`-Xmx`) or reduce `-XX:MaxGCPauseMillis` to trigger collection sooner |\n| Too few G1 concurrent threads | Increase `-XX:ConcGCThreads` |\n| App creating humongous objects frequently | Increase `-XX:G1HeapRegionSize` |\n\n**Rule of thumb:** repeated evacuation failures are a sign that the heap or\nGC concurrency settings are undersized for the allocation rate.\n",{"id":93,"difficulty":29,"q":94,"a":95},"throughput-vs-latency","How do you choose between a throughput-oriented and a latency-oriented GC?","**Throughput** = fraction of CPU time spent executing application code\n(not GC). Measured over long windows (minutes); long individual pauses are\nacceptable.\n\n**Latency** = individual GC pause duration. Matters for interactive apps,\nAPIs with SLAs, or real-time systems where a 500 ms pause is unacceptable.\n\n| GC | Optimises | Best for |\n|---|---|---|\n| Parallel GC | Throughput | Batch jobs, offline processing |\n| G1 | Balanced (default 200 ms goal) | General-purpose servers |\n| ZGC \u002F Shenandoah | Latency (\u003C 1–10 ms) | Low-latency APIs, trading, streaming |\n\n```bash\n# Batch job: maximise throughput\njava -XX:+UseParallelGC -XX:GCTimeRatio=19 BatchJob   # 95 % app, 5 % GC\n\n# API server: limit pause time\njava -XX:+UseG1GC -XX:MaxGCPauseMillis=50 ApiServer\n```\n\n**Rule of thumb:** measure your SLA requirements first — if p99 latency\nalready meets the SLA with G1, there is no reason to switch to ZGC.\n",{"id":97,"difficulty":29,"q":98,"a":99},"system-gc","What does System.gc() do and why should you avoid it?","`System.gc()` is a **hint** to the JVM to run a full GC. The JVM may ignore\nit (especially with `-XX:+DisableExplicitGC`, which is common in production).\nWhen it is honoured it triggers a **Full GC** — the most expensive GC event.\n\n```java\nSystem.gc(); \u002F\u002F hint — may trigger Full GC, may be ignored\n```\n\nWhy to avoid it:\n- Forces an expensive Full GC at an unpredictable point, causing a long\n  pause that can violate SLAs.\n- The GC is better at deciding *when* to collect than application code.\n- Commonly a sign of premature optimisation or misunderstanding of GC.\n\nLegitimate use: in benchmarking code, called before each measurement to\nstart from a clean heap. Even then, use `Runtime.getRuntime().gc()` in a\nloop and confirm GC actually ran with logs.\n\n**Rule of thumb:** never call `System.gc()` in production code; remove it\nimmediately if you find it in a code review.\n",{"id":101,"difficulty":14,"q":102,"a":103},"gc-safe-points","What is a safepoint in the JVM and how does it relate to GC?","A **safepoint** is a point in the execution of application threads at which\nthe JVM can safely inspect or modify the heap — all threads are at a\nconsistent, known state (no partially-constructed objects being referenced).\nBefore a stop-the-world GC phase, the JVM requests all threads to reach\ntheir next safepoint and halt.\n\nSafepoint checks are inserted by the JIT at method returns, backward\nbranches (loop backs), and certain other locations. A thread that is\nexecuting a long counted loop without a backward branch may delay GC by\nfailing to reach a safepoint quickly — the \"safepoint bias\" problem,\nmitigated in Java 10+ with `-XX:+UseCountedLoopSafepoints`.\n\n```\n\u002F\u002F A tight counted loop may delay safepoint:\nfor (int i = 0; i \u003C 1_000_000_000; i++) {\n    sum += array[i]; \u002F\u002F no backward branch safepoint in old JVMs\n}\n```\n\n**Rule of thumb:** if GC pause logs show suspiciously long \"time to\nsafepoint\" entries, look for long-running counted loops in the hot path.\n",20,null,{"description":11},"Java garbage collection interview questions — GC algorithms, Serial\u002FParallel\u002FG1\u002FZGC\u002FShenandoah collectors, stop-the-world pauses, Minor vs Full GC, GC tuning flags, GC logs, finalization, and avoiding common GC anti-patterns.","java\u002Fjvm-internals\u002Fgarbage-collection","JVM Internals","jvm-internals","2026-06-20","2johcH0Z2_nzkPh5ipjG3e4H_tf9-Huj2tNCW4bfxxQ",[114,118,119],{"subtopic":115,"path":116,"order":117},"Memory — Heap & Stack","\u002Fjava\u002Fjvm-internals\u002Fmemory-heap-stack",1,{"subtopic":6,"path":20,"order":12},{"subtopic":120,"path":121,"order":122},"Class Loading","\u002Fjava\u002Fjvm-internals\u002Fclassloading",3,{"path":124,"title":125},"\u002Fblog\u002Fjava-garbage-collection","Java Garbage Collection Deep Dive — G1, ZGC, Tuning, and Avoiding GC Pauses",1782244117470]