[{"data":1,"prerenderedAt":306},["ShallowReactive",2],{"topic-java-jvm-internals":3},{"framework":4,"topic":15,"subtopics":24},{"id":5,"description":6,"extension":7,"icon":8,"meta":9,"name":10,"order":11,"slug":8,"stem":12,"tier":13,"__hash__":14},"frameworks\u002Fframeworks\u002Fjava.yml","Java interview questions on language fundamentals, object-oriented design, the Collections Framework, exception handling and concurrency — a staple of backend and enterprise technical interviews.","yml","java",{},"Java",5,"frameworks\u002Fjava",1,"jcUKEcQDoMZrreMRtZYJIVpkQlB-varlzbuwmzLr7kc",{"id":16,"description":17,"extension":7,"frameworkSlug":8,"meta":18,"name":19,"order":20,"slug":21,"stem":22,"__hash__":23},"topics\u002Ftopics\u002Fjava-jvm-internals.yml","The heap and stack, garbage collection and class loading — how the Java Virtual Machine runs, manages memory and loads your code.",{},"JVM Internals",8,"jvm-internals","topics\u002Fjava-jvm-internals","3xsvKXS7o9kBEe5kuidQ3YxF-l6nDxs-vn1LZ3oXvoY",[25,135,229],{"id":26,"title":27,"body":28,"description":32,"difficulty":35,"extension":36,"framework":10,"frameworkSlug":8,"meta":37,"navigation":38,"order":13,"path":39,"questions":40,"questionsCount":127,"related":128,"seo":129,"seoDescription":130,"stem":131,"subtopic":132,"topic":19,"topicSlug":21,"updated":133,"__hash__":134},"qa\u002Fjava\u002Fjvm-internals\u002Fmemory-heap-stack.md","Memory Heap Stack",{"type":29,"value":30,"toc":31},"minimark",[],{"title":32,"searchDepth":33,"depth":33,"links":34},"",2,[],"medium","md",{},true,"\u002Fjava\u002Fjvm-internals\u002Fmemory-heap-stack",[41,46,50,54,58,62,66,70,74,79,83,87,91,95,99,103,107,111,115,119,123],{"id":42,"difficulty":43,"q":44,"a":45},"heap-vs-stack","easy","What is the difference between the heap and the stack in the JVM?","The **stack** is a per-thread memory region that stores **stack frames** —\nlocal variables, method parameters, and return addresses for each active\nmethod call. It is **LIFO** and automatically reclaimed when a frame pops.\nThe **heap** is shared across all threads and holds every **object instance**\nand every **array** created with `new`.\n\n```java\nvoid demo() {\n    int x = 42;                \u002F\u002F x lives on the stack (primitive local)\n    String s = new String(\"hi\"); \u002F\u002F the String object lives on the heap;\n                                 \u002F\u002F s (the reference) lives on the stack\n}\n```\n\n**Rule of thumb:** if it was created with `new`, it's on the heap; local\nprimitives and references themselves live on the stack.\n",{"id":47,"difficulty":43,"q":48,"a":49},"what-lives-on-stack","What exactly is stored on the stack for each method call?","Each method invocation creates a **stack frame** containing:\n- **Local variable array** — method parameters and `local` variables\n  (primitives stored by value, object references stored as pointers).\n- **Operand stack** — a work area the JVM uses to execute bytecode\n  instructions (push, pop, arithmetic).\n- **Frame data** — a reference to the runtime constant pool entry for the\n  current class and the return address.\n\n```java\nint add(int a, int b) {   \u002F\u002F a and b are in the local variable array\n    int sum = a + b;      \u002F\u002F sum is also in the local variable array\n    return sum;           \u002F\u002F frame pops; sum is gone\n}\n```\n\n**Rule of thumb:** the stack frame is the method's private scratch pad —\nit is created on call and destroyed on return.\n",{"id":51,"difficulty":43,"q":52,"a":53},"object-reference-on-stack","Does an object reference live on the stack or the heap?","The **reference variable** (the pointer) lives on the stack (or as a field\ninside another heap object), while the **object it points to** always lives\non the heap.\n\n```java\nvoid foo() {\n    StringBuilder sb = new StringBuilder(); \u002F\u002F ref 'sb' is on the stack;\n                                            \u002F\u002F the StringBuilder is on the heap\n    sb.append(\"hi\");\n}   \u002F\u002F frame pops → 'sb' gone; object may be GC'd if no other references\n```\n\n**Rule of thumb:** never confuse the address with the house — the reference\nis the address card, the object is the house.\n",{"id":55,"difficulty":35,"q":56,"a":57},"heap-regions","How is the Java heap divided internally?","The HotSpot heap uses a **generational** layout for the default GC collectors:\n\n| Region | Purpose |\n|---|---|\n| **Eden** | new objects allocated here first |\n| **Survivor 0 \u002F S1** | objects that survived one GC cycle |\n| **Old Gen (Tenured)** | long-lived objects promoted from Young Gen |\n\nEden + Survivor spaces together form the **Young Generation**. A\n**Minor GC** collects Young Gen; a **Major\u002FFull GC** also collects Old Gen.\n\n```java\n\u002F\u002F From the JVM perspective:\n\u002F\u002F new Object() → allocated in Eden\n\u002F\u002F survives N minor GCs → copied to Survivor space\n\u002F\u002F reaches tenure threshold → promoted to Old Gen\n```\n\n**Rule of thumb:** short-lived objects die young in Eden; objects that\nsurvive long enough are promoted to Old Gen where collection is expensive.\n",{"id":59,"difficulty":35,"q":60,"a":61},"metaspace","What is Metaspace and how does it differ from PermGen?","**PermGen** (Permanent Generation) was a fixed-size heap region in Java ≤ 7\nthat stored **class metadata**, interned strings, and static fields.\nIt was notorious for `OutOfMemoryError: PermGen space` when too many classes\nwere loaded (e.g., by hot-deploy in app servers).\n\nJava 8 replaced it with **Metaspace**, which lives in **native memory**\n(outside the Java heap) and grows dynamically by default.\n\n```\n# Java 7 and earlier:\n-XX:MaxPermSize=256m    # had to be tuned manually\n\n# Java 8+:\n-XX:MaxMetaspaceSize=256m  # optional cap; unlimited by default\n```\n\n**Rule of thumb:** Metaspace can't cause `PermGen` errors because it is not\non the heap — but without a cap it can exhaust native memory, so set\n`-XX:MaxMetaspaceSize` in long-running servers.\n",{"id":63,"difficulty":43,"q":64,"a":65},"stack-overflow-error","What causes a StackOverflowError?","Each thread has a fixed-size stack. When method calls nest too deeply —\nalmost always due to **unbounded recursion** — the stack runs out of space\nand the JVM throws `StackOverflowError`.\n\n```java\nint factorial(int n) {\n    return n * factorial(n - 1); \u002F\u002F no base case → infinite recursion\n}\n\u002F\u002F Throws: java.lang.StackOverflowError\n```\n\nThe default thread stack size is typically 512 KB – 1 MB (JVM-dependent).\nIt can be changed with `-Xss` (e.g., `-Xss2m`).\n\n**Rule of thumb:** `StackOverflowError` almost always means missing or\nincorrect base case in recursion; check the call chain, not the heap.\n",{"id":67,"difficulty":35,"q":68,"a":69},"outofmemoryerror-heap","What causes OutOfMemoryError: Java heap space?","The JVM throws `OutOfMemoryError: Java heap space` when it cannot allocate\na new object even after running GC and expanding the heap to `-Xmx`.\nCommon causes are **memory leaks** (objects retained unintentionally),\n**large data loads** (reading an entire file into memory), or a heap that\nis simply too small for the workload.\n\n```java\nList\u003Cbyte[]> leak = new ArrayList\u003C>();\nwhile (true) {\n    leak.add(new byte[1024 * 1024]); \u002F\u002F keeps reference → GC can't collect\n}\n\u002F\u002F java.lang.OutOfMemoryError: Java heap space\n```\n\nDiagnose with a heap dump (`-XX:+HeapDumpOnOutOfMemoryError`) and inspect\nwith MAT or VisualVM.\n\n**Rule of thumb:** `OOM: Java heap space` means either the heap is too\nsmall or something is holding references it shouldn't.\n",{"id":71,"difficulty":43,"q":72,"a":73},"xms-xmx","What do -Xms and -Xmx control?","`-Xms` sets the **initial heap size** and `-Xmx` sets the **maximum heap\nsize**. The JVM starts with `Xms` and grows up to `Xmx` as needed.\n\n```bash\njava -Xms512m -Xmx2g MyApp\n# starts with 512 MB heap, can grow to 2 GB\n```\n\nSetting them equal avoids heap-resize pauses in production (no\nexpansion\u002Fshrink cycles), at the cost of reserving the full memory upfront.\n\n**Rule of thumb:** in production containers set `-Xms` = `-Xmx` to keep\nheap size predictable and prevent GC-driven resize pauses.\n",{"id":75,"difficulty":76,"q":77,"a":78},"escape-analysis","hard","What is escape analysis and how does it affect heap allocation?","**Escape analysis** is a JIT optimization where the compiler determines\nwhether an object's reference **escapes** the method or thread it was\ncreated in. If it doesn't escape, the JVM can **allocate it on the stack**\nor **scalar-replace** it (break it into individual fields), avoiding heap\nallocation and GC pressure entirely.\n\n```java\nvoid compute() {\n    Point p = new Point(1, 2); \u002F\u002F if p never escapes this method…\n    int sum = p.x + p.y;       \u002F\u002F …JIT may replace it with two int locals\n}                              \u002F\u002F no heap allocation, no GC overhead\n```\n\nEscape analysis is enabled by default in HotSpot (`-XX:+DoEscapeAnalysis`).\nIt explains why micro-benchmarks allocating temporary objects sometimes show\nsurprisingly low GC activity.\n\n**Rule of thumb:** you can't force escape analysis, but writing short-lived\nhelper objects inside a method (not passing them out) gives the JIT the best\nchance to eliminate the allocation.\n",{"id":80,"difficulty":35,"q":81,"a":82},"stack-thread-safety","Why are local variables thread-safe without synchronization?","Each thread has its **own stack**. Local variables (including primitive\nlocals and object references local to a method) exist only in that thread's\nstack frames. No other thread can see or modify them, so there is no sharing\nand no synchronization is needed.\n\n```java\nvoid process() {\n    int counter = 0;        \u002F\u002F on this thread's stack — invisible to others\n    counter++;              \u002F\u002F safe, no volatile\u002Fsynchronized needed\n}\n```\n\nObjects on the **heap**, however, can be reachable from multiple threads via\nshared references, which is where thread-safety concerns arise.\n\n**Rule of thumb:** stack data is private to its thread; only shared heap\ndata needs synchronization.\n",{"id":84,"difficulty":76,"q":85,"a":86},"object-header","What does every Java object carry in memory beyond its fields?","Every heap object has a hidden **object header** prepended by the JVM,\ntypically **16 bytes** on a 64-bit JVM (or 12 bytes with compressed oops):\n\n| Part | Size | Purpose |\n|---|---|---|\n| **Mark word** | 8 bytes | hashCode, GC age bits, lock state, biased-locking info |\n| **Class pointer** | 4–8 bytes | pointer to the class metadata in Metaspace |\n\nAfter the header come the **instance fields**, then padding to align to 8\nbytes.\n\n```java\n\u002F\u002F Rough layout of: new Object()\n\u002F\u002F [mark word 8 bytes][class ptr 4 bytes][4 bytes padding] = 16 bytes minimum\n```\n\n**Rule of thumb:** even an empty `new Object()` costs at least 16 bytes on\nheap — object count matters for memory budgets, not just field sizes.\n",{"id":88,"difficulty":35,"q":89,"a":90},"minor-vs-major-gc","What is the difference between a Minor GC and a Full GC?","A **Minor GC** (also called a Young GC) collects only the **Young\nGeneration** (Eden + Survivor spaces). It is fast because Young Gen is\nsmall and most objects there are already dead. It causes a short\n**stop-the-world** pause.\n\nA **Full GC** (or Major GC) collects the **entire heap** — Young Gen and\nOld Gen — and also processes Metaspace. It is much slower and causes a\nlonger pause. It is triggered when Old Gen fills up or when\n`System.gc()` is called.\n\n```\n\u002F\u002F Triggering a Full GC deliberately (rarely advisable in production):\nSystem.gc();  \u002F\u002F hint only; JVM may ignore it\n```\n\n**Rule of thumb:** frequent Full GCs are a red flag — they signal that\nobjects are promoted to Old Gen faster than it can be cleaned, often due\nto a memory leak or undersized heap.\n",{"id":92,"difficulty":35,"q":93,"a":94},"string-pool","Where does the String pool live and how does it affect memory?","The **String pool** (interned String table) lives on the **heap** in Java 7+\n(it was in PermGen in Java 6 and earlier). String literals are automatically\ninterned; calling `String.intern()` manually adds a string to the pool.\n\n```java\nString a = \"hello\";           \u002F\u002F from the string pool\nString b = \"hello\";           \u002F\u002F same pooled object\nString c = new String(\"hello\"); \u002F\u002F new heap object, NOT pooled\nSystem.out.println(a == b);   \u002F\u002F true  — same pool reference\nSystem.out.println(a == c);   \u002F\u002F false — c is a distinct heap object\n```\n\nBecause the pool is on the heap it is subject to GC, so interned strings\nthat are no longer referenced can be collected (unlike PermGen, which was\nnever GC'd in Java 6).\n\n**Rule of thumb:** prefer `equals()` for string comparison; `==` only works\nreliably on pool references (literals or explicitly interned strings).\n",{"id":96,"difficulty":76,"q":97,"a":98},"compressed-oops","What are compressed ordinary object pointers (compressed oops)?","On a 64-bit JVM, a raw pointer to a heap object is 8 bytes. **Compressed\noops** (`-XX:+UseCompressedOops`, enabled by default when heap ≤ 32 GB)\nstore object references as **32-bit values** by encoding the pointer as a\n**word offset** (address >> 3). The JVM shifts the value back to a full\n64-bit address at use time, transparently.\n\n```\n# Enabled automatically when -Xmx \u003C= ~32 GB:\n-XX:+UseCompressedOops      # compress heap references\n-XX:+UseCompressedClassPointers  # also compress class pointers in header\n```\n\nThis reduces per-object memory by shrinking every reference field and the\nclass pointer in the header from 8 → 4 bytes, which often cuts heap usage\nby 20–30 %.\n\n**Rule of thumb:** keep heap below 32 GB to retain compressed oops; crossing\nthat boundary can actually increase memory usage because every reference\ngrows from 4 to 8 bytes.\n",{"id":100,"difficulty":76,"q":101,"a":102},"thread-local-allocation-buffer","What is a TLAB (Thread-Local Allocation Buffer)?","Allocating on the heap naively would require synchronization on every `new`\nbecause all threads share Eden. The JVM avoids this by giving each thread\nits own private chunk of Eden called a **TLAB**. Allocations within the\nthread just bump a pointer inside the TLAB — no locking needed.\n\n```\n\u002F\u002F Conceptually:\nThread T1 → has TLAB [0x1000 – 0x2000]\nThread T2 → has TLAB [0x2000 – 0x3000]\nnew Foo() in T1 → bumps T1's pointer; no synchronization\n```\n\nWhen a TLAB is exhausted the thread requests a fresh one from Eden (under a\nbrief lock). Objects too large for any TLAB are allocated directly in Eden\nor Old Gen.\n\n**Rule of thumb:** TLABs are why Java object allocation is nearly as cheap\nas incrementing a pointer — the cost shows up at GC time, not at `new`.\n",{"id":104,"difficulty":35,"q":105,"a":106},"soft-weak-phantom-references","How do soft, weak, and phantom references interact with heap memory?","Java provides reference types that let the **GC reclaim objects** under\ndifferent urgency levels, avoiding `OutOfMemoryError` for caches:\n\n| Type | Cleared when | Use case |\n|---|---|---|\n| `SoftReference\u003CT>` | GC needs memory (before OOM) | memory-sensitive caches |\n| `WeakReference\u003CT>` | next GC, regardless of memory | canonicalizing maps (`WeakHashMap`) |\n| `PhantomReference\u003CT>` | after finalization, just before reclaim | post-mortem cleanup \u002F resource release |\n\n```java\nCache\u003CK, V> cache = new LinkedHashMap\u003C>();\nSoftReference\u003CBigData> ref = new SoftReference\u003C>(loadData());\nBigData d = ref.get(); \u002F\u002F null if GC has already cleared it\n```\n\n**Rule of thumb:** use `SoftReference` for caches you want the GC to evict\nunder pressure, `WeakReference` when you don't want to prevent collection,\nand `PhantomReference` only when you need a GC notification callback.\n",{"id":108,"difficulty":35,"q":109,"a":110},"gc-roots","What are GC roots and why do they matter?","**GC roots** are the starting points from which the garbage collector traces\nlive object graphs. Any object reachable from a GC root is considered live\nand will not be collected. Common GC roots include:\n\n- Local variables and operand-stack entries in **active stack frames**\n- **Static fields** of loaded classes\n- References held by **JNI** (native code)\n- Objects referenced by **active threads** themselves\n\n```java\nstatic List\u003CObject> cache = new ArrayList\u003C>(); \u002F\u002F static field = GC root\n\u002F\u002F anything added to 'cache' will never be GC'd while the class is loaded\n```\n\nMemory leaks in Java are almost always objects that remain reachable from a\nGC root unintentionally (e.g., a static list that grows forever).\n\n**Rule of thumb:** a Java memory leak is not \"memory the GC can't see\" but\n\"memory the GC won't collect because a root still points to it.\"\n",{"id":112,"difficulty":35,"q":113,"a":114},"stack-size-tuning","When and how would you tune the thread stack size?","The default thread stack size (typically 512 KB on 64-bit Linux) is\nsufficient for most apps. You'd increase it with `-Xss` if deep but\nlegitimate recursion (e.g., recursive descent parsers, deep call chains in\nframeworks) causes `StackOverflowError`.\n\n```bash\njava -Xss2m MyApp   # 2 MB stack per thread\n```\n\nBe careful: each thread gets its own stack, so doubling `-Xss` in an app\nwith 500 threads doubles the native memory reserved for stacks. This memory\ncomes from **native (off-heap) memory**, not from the Java heap.\n\n**Rule of thumb:** increase `-Xss` only when you own the recursion and\ncan't refactor it to iteration; otherwise fix the algorithm.\n",{"id":116,"difficulty":35,"q":117,"a":118},"native-memory","What is native (off-heap) memory in the JVM and what uses it?","**Native memory** (also called off-heap memory) is memory the JVM allocates\nfrom the OS directly, outside the Java heap. It is not managed by the\ngarbage collector. Key consumers include:\n\n| Consumer | Notes |\n|---|---|\n| Thread stacks | one per thread × `-Xss` |\n| Metaspace | class metadata |\n| Code cache | JIT-compiled native code |\n| Direct ByteBuffers | `ByteBuffer.allocateDirect()` |\n| GC data structures | card tables, remembered sets |\n\n```java\nByteBuffer buf = ByteBuffer.allocateDirect(64 * 1024 * 1024); \u002F\u002F 64 MB off-heap\n\u002F\u002F not counted in -Xmx, freed when buf is GC'd or explicitly with Cleaner\n```\n\n**Rule of thumb:** if a process's RSS grows well beyond `-Xmx`, the culprit\nis usually native memory — Metaspace, thread stacks, or direct buffers.\n",{"id":120,"difficulty":35,"q":121,"a":122},"heap-dump-analysis","How do you take and analyze a heap dump to diagnose a memory leak?","Capture a heap dump while the leak is active, then inspect it with a tool\nlike Eclipse MAT or VisualVM to find the **dominator tree** — the objects\nretaining the most memory.\n\n```bash\n# Enable automatic dump on OOM:\njava -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=\u002Ftmp\u002Fdump.hprof MyApp\n\n# Manual dump of running process (PID 12345):\njmap -dump:live,format=b,file=\u002Ftmp\u002Fdump.hprof 12345\n```\n\nIn MAT, the **\"Leak Suspects\" report** identifies objects accumulating in\nmemory and traces the shortest path back to a GC root — that path reveals\nwhy the object isn't being collected.\n\n**Rule of thumb:** always capture the heap dump while the problem is\noccurring; a dump taken after a restart won't show the leaked objects.\n",{"id":124,"difficulty":35,"q":125,"a":126},"generational-hypothesis","What is the weak generational hypothesis and why does it guide GC design?","The **weak generational hypothesis** is the empirical observation that\n**most objects die young** — they are allocated, used briefly, and become\ngarbage within a few milliseconds. A minority of objects survive long term.\n\nThis drives the **generational GC design**: allocate cheaply in a small,\nfrequently-collected Young Gen where most objects die without ever touching\nOld Gen. Only survivors get promoted to the expensive-to-collect Old Gen.\n\n```\nTypical production allocation profile:\n  ~90 % of objects die before their first Minor GC\n  ~5–9 % get promoted after a few GC cycles\n  ~1 % live for the application's lifetime (caches, singletons)\n```\n\n**Rule of thumb:** generational GC works well when you respect the\nhypothesis — minimize long-lived object graphs and avoid putting ephemeral\nobjects in static caches.\n",21,null,{"description":32},"Java JVM memory interview questions — heap vs stack, stack frames, object allocation, Eden\u002FSurvivor\u002FOld Gen regions, Metaspace, escape analysis, OutOfMemoryError, and StackOverflowError.","java\u002Fjvm-internals\u002Fmemory-heap-stack","Memory — Heap & Stack","2026-06-20","WDcFt9dt33P6sHnOxhkvhG5xvOpguKDxfTFB1GY9uCM",{"id":136,"title":137,"body":138,"description":32,"difficulty":76,"extension":36,"framework":10,"frameworkSlug":8,"meta":142,"navigation":38,"order":33,"path":143,"questions":144,"questionsCount":224,"related":128,"seo":225,"seoDescription":226,"stem":227,"subtopic":137,"topic":19,"topicSlug":21,"updated":133,"__hash__":228},"qa\u002Fjava\u002Fjvm-internals\u002Fgarbage-collection.md","Garbage Collection",{"type":29,"value":139,"toc":140},[],{"title":32,"searchDepth":33,"depth":33,"links":141},[],{},"\u002Fjava\u002Fjvm-internals\u002Fgarbage-collection",[145,149,153,157,161,165,169,173,177,181,185,188,192,196,200,204,208,212,216,220],{"id":146,"difficulty":43,"q":147,"a":148},"what-is-gc","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":150,"difficulty":35,"q":151,"a":152},"mark-and-sweep","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":154,"difficulty":35,"q":155,"a":156},"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":158,"difficulty":43,"q":159,"a":160},"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":162,"difficulty":43,"q":163,"a":164},"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":166,"difficulty":35,"q":167,"a":168},"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":170,"difficulty":35,"q":171,"a":172},"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":174,"difficulty":76,"q":175,"a":176},"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":178,"difficulty":76,"q":179,"a":180},"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":182,"difficulty":35,"q":183,"a":184},"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":108,"difficulty":35,"q":186,"a":187},"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":189,"difficulty":35,"q":190,"a":191},"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":193,"difficulty":35,"q":194,"a":195},"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":197,"difficulty":35,"q":198,"a":199},"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":201,"difficulty":35,"q":202,"a":203},"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":205,"difficulty":35,"q":206,"a":207},"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":209,"difficulty":76,"q":210,"a":211},"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":213,"difficulty":35,"q":214,"a":215},"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":217,"difficulty":35,"q":218,"a":219},"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":221,"difficulty":76,"q":222,"a":223},"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,{"description":32},"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","2johcH0Z2_nzkPh5ipjG3e4H_tf9-Huj2tNCW4bfxxQ",{"id":230,"title":231,"body":232,"description":32,"difficulty":76,"extension":36,"framework":10,"frameworkSlug":8,"meta":236,"navigation":38,"order":237,"path":238,"questions":239,"questionsCount":300,"related":128,"seo":301,"seoDescription":302,"stem":303,"subtopic":304,"topic":19,"topicSlug":21,"updated":133,"__hash__":305},"qa\u002Fjava\u002Fjvm-internals\u002Fclassloading.md","Classloading",{"type":29,"value":233,"toc":234},[],{"title":32,"searchDepth":33,"depth":33,"links":235},[],{},3,"\u002Fjava\u002Fjvm-internals\u002Fclassloading",[240,244,248,252,256,260,264,268,272,276,280,284,288,292,296],{"id":241,"difficulty":43,"q":242,"a":243},"what-is-classloading","What is class loading in the JVM?","**Class loading** is the process by which the JVM reads a `.class` file\n(bytecode), parses it, and creates the corresponding `java.lang.Class`\nobject in memory. A class is loaded **on demand** the first time it is\nreferenced by running code — not upfront at JVM startup.\n\n```java\n\u002F\u002F When this line executes for the first time:\nMyService svc = new MyService();\n\u002F\u002F The JVM loads MyService.class if it hasn't been loaded yet,\n\u002F\u002F resolves its dependencies, initialises static fields, then calls new.\n```\n\n**Rule of thumb:** classes are loaded lazily; the JVM only reads a\n`.class` file the first time that class is actually needed.\n",{"id":245,"difficulty":35,"q":246,"a":247},"classloader-types","What are the built-in ClassLoaders in the JVM and what does each load?","The JVM ships with a **hierarchy of ClassLoaders**:\n\n| ClassLoader | Java 8 name | Java 9+ name | Loads |\n|---|---|---|---|\n| Bootstrap | Bootstrap CL | Bootstrap CL | Core JDK classes (`java.lang.*`, `java.util.*`) from `rt.jar` \u002F `java.base` module |\n| Extension \u002F Platform | Extension CL | Platform CL | `javax.*`, security providers; in Java 9+ it loads named JDK modules not in `java.base` |\n| Application | App \u002F System CL | App CL | Classes from the user **classpath** (`-cp`) or module path |\n\n```java\nSystem.out.println(String.class.getClassLoader());        \u002F\u002F null (Bootstrap)\nSystem.out.println(ClassLoader.getSystemClassLoader());   \u002F\u002F AppClassLoader\nSystem.out.println(MyApp.class.getClassLoader());         \u002F\u002F AppClassLoader\n```\n\n**Rule of thumb:** `null` for a class's ClassLoader means it was loaded by\nthe Bootstrap ClassLoader — the JVM doesn't expose a Java object for it.\n",{"id":249,"difficulty":35,"q":250,"a":251},"delegation-model","What is the parent-delegation model for class loading?","Before loading a class, a ClassLoader **delegates to its parent** first.\nIf the parent (or its parent) can load the class, that result is used.\nOnly if the parent chain cannot find the class does the current loader\nattempt to load it itself. This ensures that core JDK classes loaded by\nBootstrap always take precedence.\n\n```\nAppClassLoader.loadClass(\"com.example.MyClass\")\n    → delegates to PlatformClassLoader\n        → delegates to BootstrapClassLoader\n            → not found in rt.jar \u002F java.base\n        ← BootstrapClassLoader returns null\n    ← PlatformClassLoader returns null\n→ AppClassLoader loads from classpath ✓\n```\n\n**Rule of thumb:** parent-delegation prevents a rogue `java\u002Flang\u002FString.class`\non the classpath from shadowing the real `String` — the Bootstrap loader\nalways wins for JDK classes.\n",{"id":253,"difficulty":35,"q":254,"a":255},"loading-linking-initialisation","What are the three phases of class loading — loading, linking, and initialisation?","**Loading** — reads the binary class data (from a file, JAR, network, etc.)\nand creates the `Class\u003C?>` object. The bytecode is not yet verified.\n\n**Linking** — three sub-steps:\n1. **Verification** — checks that the bytecode is structurally correct and\n   won't violate JVM safety guarantees.\n2. **Preparation** — allocates memory for static fields and sets them to\n   their **default values** (`0`, `null`, `false`) — *not* the values in\n   initialiser code yet.\n3. **Resolution** — resolves symbolic references (class names, method\n   descriptors) in the constant pool to actual memory addresses (optional\n   at link time; may be deferred).\n\n**Initialisation** — runs the class's `\u003Cclinit>` method (static initialisers\nand static field assignments) **exactly once**, the first time the class\nis actively used.\n\n```java\nclass Config {\n    static int LIMIT = Integer.parseInt(System.getenv(\"LIMIT\")); \u002F\u002F runs in \u003Cclinit>\n}\n```\n\n**Rule of thumb:** static fields are zeroed during *preparation*, then set\nto their real values during *initialisation* — never assume a static field\nholds its declared value before the class is initialised.\n",{"id":257,"difficulty":35,"q":258,"a":259},"class-identity","What is class identity in the JVM and why can two classes with the same name be different?","A class in the JVM is identified by **both its fully qualified name and its\nClassLoader**. Two `Class\u003C?>` objects are the **same class** only if both\nthe name and the ClassLoader instance match.\n\nThis is why frameworks like Java EE application servers, OSGi, and hot-deploy\nsystems use **separate ClassLoaders per deployment** — each application gets\nits own `com.example.Service`, isolated from others.\n\n```java\nClassLoader cl1 = new URLClassLoader(urls);\nClassLoader cl2 = new URLClassLoader(urls);\nClass\u003C?> a = cl1.loadClass(\"com.example.Foo\");\nClass\u003C?> b = cl2.loadClass(\"com.example.Foo\");\nSystem.out.println(a == b);              \u002F\u002F false — different loaders\nSystem.out.println(a.equals(b));         \u002F\u002F false\n\u002F\u002F Casting between them would throw ClassCastException\n```\n\n**Rule of thumb:** class identity = (fully-qualified name, ClassLoader);\nthe same `.class` file loaded by two different ClassLoaders produces two\nincompatible types.\n",{"id":261,"difficulty":35,"q":262,"a":263},"classnotfoundexception-vs-noclassdeffounderror","What is the difference between ClassNotFoundException and NoClassDefFoundError?","| | `ClassNotFoundException` | `NoClassDefFoundError` |\n|---|---|---|\n| Type | Checked exception | Error (unchecked) |\n| When | `Class.forName()`, `ClassLoader.loadClass()` called explicitly and the class is not on the classpath | Class was available at **compile time** but missing at **runtime** (e.g., JAR not deployed) |\n| Cause | Developer error: typo in class name, missing dependency at runtime | Deployment error: class was compiled against a JAR that is absent at runtime |\n\n```java\n\u002F\u002F ClassNotFoundException — explicit dynamic load\nClass.forName(\"com.mysql.jdbc.Driver\"); \u002F\u002F throws if MySQL JAR is missing\n\n\u002F\u002F NoClassDefFoundError — implicit reference to a class present at compile time\n\u002F\u002F but missing at runtime — JVM throws this from its own resolution code\n```\n\n**Rule of thumb:** `ClassNotFoundException` = you asked for a class by name\nand it wasn't there; `NoClassDefFoundError` = the JVM needed a class while\nloading another and couldn't find it.\n",{"id":265,"difficulty":76,"q":266,"a":267},"custom-classloader","Why would you write a custom ClassLoader and how do you do it?","Custom ClassLoaders let you load classes from non-standard sources:\nencrypted JARs, databases, over a network, generated bytecode at runtime,\nor isolated namespaces (plugins, hot-reload, OSGi bundles).\n\n```java\npublic class EncryptedClassLoader extends ClassLoader {\n    private final Path jarPath;\n\n    EncryptedClassLoader(Path p, ClassLoader parent) {\n        super(parent);           \u002F\u002F always wire up the parent for delegation\n        this.jarPath = p;\n    }\n\n    @Override\n    protected Class\u003C?> findClass(String name) throws ClassNotFoundException {\n        byte[] decrypted = decrypt(readBytesFromJar(jarPath, name));\n        return defineClass(name, decrypted, 0, decrypted.length);\n    }\n}\n```\n\nOverride `findClass()`, not `loadClass()`, to preserve the parent-delegation\nmodel. Call `defineClass()` with the raw bytecode to register the class with\nthe JVM.\n\n**Rule of thumb:** override `findClass()`, not `loadClass()` — breaking\ndelegation opens the door to shadowing core JDK classes.\n",{"id":269,"difficulty":76,"q":270,"a":271},"class-unloading","When does the JVM unload a class and why is this important for dynamic systems?","A class can be **unloaded** (and its `Class\u003C?>` object GC'd) only when\nits **ClassLoader** instance becomes unreachable. The Bootstrap ClassLoader\nnever becomes unreachable, so core JDK classes are never unloaded. Classes\nloaded by the Application ClassLoader are effectively permanent too (the\nloader lives as long as the JVM).\n\nClasses *can* be unloaded when:\n- They were loaded by a **custom ClassLoader** (e.g., a plugin loader).\n- All Class objects, all instances, and the ClassLoader itself have no\n  strong references.\n\n```java\nURLClassLoader pluginLoader = new URLClassLoader(urls, parent);\nClass\u003C?> pluginClass = pluginLoader.loadClass(\"com.plugin.Plugin\");\n\u002F\u002F ... use the plugin ...\npluginLoader.close();    \u002F\u002F no more strong ref to loader or class\npluginLoader = null;\n\u002F\u002F Now the GC can unload the plugin class and reclaim Metaspace\n```\n\nMetaspace leaks in hot-deploy scenarios (e.g., Tomcat web app reload) are\nalmost always caused by something outside the webapp's ClassLoader still\nholding a reference to the loader or one of its classes.\n\n**Rule of thumb:** if Metaspace grows after each hot-deploy, hunt for\nreferences to the old ClassLoader held in static fields or thread locals.\n",{"id":273,"difficulty":35,"q":274,"a":275},"static-initialiser","What guarantees does the JVM provide about class initialisation and static initialisers?","The JVM guarantees that a class's `\u003Cclinit>` (static initialiser block and\nstatic field assignments) runs **exactly once**, **before** any instance of\nthe class is created or any static member is accessed, and is\n**thread-safe** — the JVM uses an internal lock so that even if two threads\nrace to initialise the same class, only one runs `\u003Cclinit>` and the other\nwaits.\n\n```java\nclass Singleton {\n    private static final Singleton INSTANCE = new Singleton(); \u002F\u002F safe, runs once\n\n    static {\n        System.out.println(\"Initialising Singleton\");\n    }\n}\n```\n\nThis guarantee is what makes the **Initialization-on-demand Holder** idiom\nthread-safe without explicit synchronisation:\n\n```java\nclass Holder {\n    private Holder() {}\n    private static class Inner { static final Holder INSTANCE = new Holder(); }\n    static Holder getInstance() { return Inner.INSTANCE; }\n}\n\u002F\u002F Inner is not loaded until getInstance() is first called;\n\u002F\u002F JVM ensures thread-safe single initialisation\n```\n\n**Rule of thumb:** static initialisers are guaranteed to run once and\nsafely — this is the foundation of the holder singleton pattern.\n",{"id":277,"difficulty":43,"q":278,"a":279},"class-forname","What does Class.forName() do and what are its overloads?","`Class.forName(String name)` loads and **initialises** the named class\nusing the calling class's ClassLoader. The two-argument overload gives you\ncontrol over both:\n\n```java\n\u002F\u002F Simple form: loads + initialises using caller's ClassLoader\nClass\u003C?> clazz = Class.forName(\"com.example.MyPlugin\");\n\n\u002F\u002F Full control: Class.forName(name, initialize, loader)\nClass\u003C?> lazy = Class.forName(\"com.example.MyPlugin\",\n                              false,              \u002F\u002F don't initialise yet\n                              customLoader);      \u002F\u002F use this ClassLoader\n```\n\nThe classic use case is **JDBC driver registration** (pre-JDBC 4):\n`Class.forName(\"com.mysql.jdbc.Driver\")` loaded and initialised the driver\nclass, whose static block registered it with `DriverManager`. JDBC 4+\nuses `ServiceLoader` to do this automatically.\n\n**Rule of thumb:** `Class.forName(name)` = load + initialise;\n`Class.forName(name, false, loader)` = load only, defer initialisation.\n",{"id":281,"difficulty":76,"q":282,"a":283},"module-system-classloading","How did the Java 9 module system change class loading?","Before Java 9, the JDK was a monolithic `rt.jar` loaded by Bootstrap.\nJava 9 introduced the **module system (JPMS)**, splitting the JDK into\nnamed modules (`java.base`, `java.sql`, `java.logging`, etc.).\n\nKey changes:\n- **Bootstrap ClassLoader** now loads only `java.base` and a few core\n  modules; no more `rt.jar`.\n- **Platform ClassLoader** (renamed from Extension CL) loads the remaining\n  JDK modules.\n- Module boundaries enforce **strong encapsulation** — a module must\n  explicitly `exports` a package for code in other modules to access it;\n  deep reflection into non-exported packages requires `--add-opens`.\n\n```bash\n# Allow reflection into java.base internals (needed by some frameworks):\njava --add-opens java.base\u002Fjava.lang=ALL-UNNAMED -jar myapp.jar\n```\n\nClass loading *mechanics* (parent delegation, `defineClass`) are unchanged;\nwhat changed is the *source* of classes and what is accessible.\n\n**Rule of thumb:** JPMS changes *where* classes come from and *what* is\naccessible; the fundamental ClassLoader delegation chain still applies.\n",{"id":285,"difficulty":35,"q":286,"a":287},"serviceloader","What is ServiceLoader and how does it relate to class loading?","`java.util.ServiceLoader` is a lightweight **plugin \u002F SPI mechanism** that\ndynamically discovers and loads implementations of an interface registered\nin `META-INF\u002Fservices\u002F\u003Cinterface-name>` (or via `module-info.java`\n`provides` in modules).\n\n```java\n\u002F\u002F In META-INF\u002Fservices\u002Fcom.example.Plugin:\n\u002F\u002F com.example.impl.FooPlugin\n\nServiceLoader\u003CPlugin> loader = ServiceLoader.load(Plugin.class);\nfor (Plugin p : loader) {\n    p.execute(); \u002F\u002F each registered implementation is loaded and instantiated\n}\n```\n\nJDBC 4.0 drivers use `ServiceLoader` to auto-register without\n`Class.forName()`. Frameworks like SLF4J, Jackson, and JCA rely on it\nfor extensibility.\n\n**Rule of thumb:** `ServiceLoader` is the standard way to decouple an API\nfrom its implementation — prefer it over `Class.forName()` for plugin\narchitectures.\n",{"id":289,"difficulty":76,"q":290,"a":291},"classloader-leak","What is a ClassLoader leak and how do you detect and fix it?","A **ClassLoader leak** occurs when a custom ClassLoader (e.g., a web app\nloader in Tomcat) cannot be garbage collected after the app is undeployed\nbecause something outside the loader still holds a reference to it or to\na class\u002Fobject loaded by it. Because the loader can't be GC'd, neither can\nany of its classes — Metaspace grows with each redeploy.\n\nCommon causes:\n- **ThreadLocal values** whose type was loaded by the webapp loader, not\n  cleared before undeployment.\n- **Static fields in a shared library** (loaded by the parent ClassLoader)\n  holding a reference to an object of a webapp class.\n- **JDBC drivers** registered with `DriverManager` and never deregistered.\n- **Logging frameworks** holding class references in static config.\n\n```java\n\u002F\u002F Fix: deregister on shutdown\n@Override\npublic void contextDestroyed(ServletContextEvent sce) {\n    Enumeration\u003CDriver> drivers = DriverManager.getDrivers();\n    while (drivers.hasMoreElements()) DriverManager.deregisterDriver(drivers.nextElement());\n    \u002F\u002F Also clear any ThreadLocals your code set\n}\n```\n\nDetect with a heap dump: look for `ClassLoader` instances that should have\nbeen unloaded; trace their retaining references with Eclipse MAT.\n\n**Rule of thumb:** every object allocated in a web app that lives beyond\nthe request must be cleaned up in a `ServletContextListener.contextDestroyed`\n— otherwise it anchors the ClassLoader forever.\n",{"id":293,"difficulty":35,"q":294,"a":295},"bootstrap-classloader","Why does String.class.getClassLoader() return null?","The **Bootstrap ClassLoader** is implemented in native code (C\u002FC++) inside\nthe JVM itself, not as a Java class. It has no Java object representation,\nso the JVM signals \"loaded by Bootstrap\" by returning **`null`** from\n`getClassLoader()`.\n\n```java\nSystem.out.println(String.class.getClassLoader());         \u002F\u002F null\nSystem.out.println(int.class.getClassLoader());            \u002F\u002F null (primitive)\nSystem.out.println(MyApp.class.getClassLoader());          \u002F\u002F sun.misc.Launcher$AppClassLoader\n```\n\nThis is a conventional signal, not an error. Code that uses ClassLoaders\nmust handle `null` and interpret it as Bootstrap:\n\n```java\nClassLoader cl = SomeClass.class.getClassLoader();\nClassLoader toUse = (cl != null) ? cl : ClassLoader.getSystemClassLoader();\n```\n\n**Rule of thumb:** `null` ClassLoader = Bootstrap = JDK core class; always\nnull-check before using a class's ClassLoader as an argument.\n",{"id":297,"difficulty":76,"q":298,"a":299},"hotswap-and-dynamic-reloading","How does Java support hot-swapping or dynamic class reloading?","The standard JVM supports limited **HotSwap** via JPDA (Java Platform\nDebugger Architecture) — you can redefine a class's method bodies at\nruntime in a debug session without restarting. It cannot change class\nstructure (fields, superclasses, interfaces).\n\nFor full hot-reload (new fields, new classes, new hierarchies), the two\napproaches are:\n\n1. **Reload via a new ClassLoader** — create a fresh `URLClassLoader`,\n   discard the old loader and all its instances, let GC clean up.\n   Used by Tomcat, OSGi, and Java EE containers.\n2. **Bytecode manipulation agents** — tools like **JRebel** or\n   **HotswapAgent** use the `java.lang.instrument` API to redefine class\n   bytecode at runtime, including structural changes. Requires a `-javaagent`\n   flag at startup.\n\n```bash\njava -javaagent:hotswap-agent.jar MyApp\n```\n\n**Rule of thumb:** for dev-time hot reload of full structural changes use\na ClassLoader-per-deployment strategy or an instrumentation agent; for\nminor method-body fixes in a running debugger, JPDA HotSwap is sufficient.\n",15,{"description":32},"Java class loading interview questions — ClassLoader hierarchy, delegation model, loading\u002Flinking\u002Finitialisation phases, custom ClassLoaders, class unloading, ClassNotFoundException vs NoClassDefFoundError, and module system changes.","java\u002Fjvm-internals\u002Fclassloading","Class Loading","K2ocnQi7twugrD-1hx8TJCVNGyuBOlFfy2lyxSPDQ14",1782244097309]