[{"data":1,"prerenderedAt":131},["ShallowReactive",2],{"qa-\u002Fjava\u002Fjvm-internals\u002Fmemory-heap-stack":3},{"page":4,"siblings":119,"blog":128},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":109,"related":110,"seo":111,"seoDescription":112,"stem":113,"subtopic":114,"topic":115,"topicSlug":116,"updated":117,"__hash__":118},"qa\u002Fjava\u002Fjvm-internals\u002Fmemory-heap-stack.md","Memory Heap Stack",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"medium","md","Java","java",{},true,1,"\u002Fjava\u002Fjvm-internals\u002Fmemory-heap-stack",[23,28,32,36,40,44,48,52,56,61,65,69,73,77,81,85,89,93,97,101,105],{"id":24,"difficulty":25,"q":26,"a":27},"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":29,"difficulty":25,"q":30,"a":31},"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":33,"difficulty":25,"q":34,"a":35},"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":37,"difficulty":14,"q":38,"a":39},"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":41,"difficulty":14,"q":42,"a":43},"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":45,"difficulty":25,"q":46,"a":47},"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":49,"difficulty":14,"q":50,"a":51},"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":53,"difficulty":25,"q":54,"a":55},"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":57,"difficulty":58,"q":59,"a":60},"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":62,"difficulty":14,"q":63,"a":64},"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":66,"difficulty":58,"q":67,"a":68},"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":70,"difficulty":14,"q":71,"a":72},"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":74,"difficulty":14,"q":75,"a":76},"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":78,"difficulty":58,"q":79,"a":80},"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":82,"difficulty":58,"q":83,"a":84},"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":86,"difficulty":14,"q":87,"a":88},"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":90,"difficulty":14,"q":91,"a":92},"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":94,"difficulty":14,"q":95,"a":96},"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":98,"difficulty":14,"q":99,"a":100},"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":102,"difficulty":14,"q":103,"a":104},"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":106,"difficulty":14,"q":107,"a":108},"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":11},"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","JVM Internals","jvm-internals","2026-06-20","WDcFt9dt33P6sHnOxhkvhG5xvOpguKDxfTFB1GY9uCM",[120,121,124],{"subtopic":114,"path":21,"order":20},{"subtopic":122,"path":123,"order":12},"Garbage Collection","\u002Fjava\u002Fjvm-internals\u002Fgarbage-collection",{"subtopic":125,"path":126,"order":127},"Class Loading","\u002Fjava\u002Fjvm-internals\u002Fclassloading",3,{"path":129,"title":130},"\u002Fblog\u002Fjava-jvm-memory-heap-stack","Java JVM Memory Explained — Heap, Stack, and Everything In Between",1782244117439]