skip to content

Runtime Memory Areas

Where a running program's memory actually sits — heap generations, per-thread stacks, class metadata, program counters, and off-heap regions — and which parts are shared versus per-thread. Interviewers ask because almost every OutOfMemoryError or StackOverflowError question is really a question about which area filled up.

on this pageshow

explore

questions

30

Trace what happens to a newly allocated Java object that stays reachable across several young-generation collections: where does it physically live at each step, what copies it, and what eventually moves it into the old generation?

level: juniorimportance: must knowfreq 62%

answer

  1. eden → survivor (copy) → survivor → old
  2. age bits in the mark word, max 15
  3. copy cost ∝ survivors, not garbage
  4. empty *to* survivor always reserved
  5. overflow or huge object ⇒ straight to old

basics

~20 s

It is allocated in eden. Each young collection copies the still-live object into the currently empty survivor space and increments its age. After surviving enough collections (age reaching the tenuring threshold) it is copied into the old generation instead — that copy is called promotion.

solid answer

~50 s

A new object is allocated in eden (normally in the thread's own allocation buffer). When eden fills, a young collection runs: the collector finds objects reachable from the roots plus from old-to-young references, and **copies** them into the empty survivor space (the "to" space). Eden and the previously used survivor space are then declared empty in one step — nothing is touched for dead objects, which is why the collection costs time proportional to what *survived*, not to what was allocated. Each copy increments an age counter kept in the object's header. On each subsequent young collection the object is copied again, ping-ponging between the two survivor spaces, until its age reaches the tenuring threshold; at that point it is copied into the old generation instead — promotion. An object can also skip the ladder: if the survivor space cannot hold everything that survived, the overflow goes straight to the old generation, and very large objects may be allocated there directly.

go deeper

for a junior

Be able to name the sequence out loud — eden, survivor copy, age increments, promotion to old — and state that only live objects are copied.

for a middle

Add the mechanics: the age counter lives in the object header, the two survivor spaces alternate roles, and overflow or the adaptive threshold can promote earlier than the configured maximum.

for a senior

Connect the path to observable symptoms: survivors overflowing produce old-generation growth without a growing live set, and repeated copying of medium-lived data shows up as young-pause duration scaling with survivor volume.

for a principal

Frame it as a cost model — young pauses cost O(survivors), old-generation collections cost far more, and every design or sizing decision is about keeping data from crossing that boundary before it has earned it.

## The starting point: eden The young generation of a HotSpot heap is split into one **eden** space and two **survivor** spaces (conventionally called *from* and *to*). Almost every object begins life in eden. Allocation there is a pointer bump inside a thread-local buffer, so it is extremely cheap; the cost of the generational design is paid later, at collection time. ## What a young (minor) collection actually does When eden cannot satisfy an allocation, a young collection is triggered. It is a **copying** (evacuating) collector, not a mark-and-sweep of the whole heap. The steps are: 1. Enumerate the roots — thread stacks, static fields, JNI handles and so on — **plus** the recorded old-to-young references (see the remembered-set mechanism, which is what lets this step skip the old generation entirely). 2. Trace only into the young generation and **copy** every live object found into the currently empty survivor space, fixing up references to the new addresses. 3. Declare eden and the survivor space that was in use to be entirely free. The crucial property: dead objects are never visited, never touched, never "freed" individually. Reclaiming them costs nothing. The work is proportional to the volume of data that *survived* the collection. That is exactly why the design pays off when most objects die young. After the collection the two survivor spaces swap roles: the one just filled becomes *from* for the next cycle, and the emptied one becomes *to*. At all times one survivor space is empty, which is the space reserved for the next copy. ## Aging Every time an object is copied during a young collection its **age** is incremented. HotSpot stores this age in a few bits of the object header's mark word — historically 4 bits, which is why the maximum age is 15. An object with age 3 has survived three young collections. When the age reaches the **tenuring threshold**, the collector copies the object into the **old generation** instead of into a survivor space. That transfer is called *promotion* or *tenuring*. Once promoted, the object is no longer visited by young collections at all; it will only be reclaimed by an old-generation or full collection. ## Ways to skip the ladder The eden → survivor → survivor → old path is the normal case, not the only one: - **Survivor overflow.** The *to* survivor space is a fixed, comparatively small region. If more data survives than fits, the excess is promoted directly to the old generation regardless of age. This is the main mechanism behind *premature promotion*. - **Threshold collapse.** Collectors compute an adaptive threshold from the actual age distribution: if the survivors already overflow the target occupancy at a low age, the effective threshold drops, and objects tenure after one or two collections. - **Large objects.** Some collectors allocate objects above a size threshold straight into the old generation, because copying them repeatedly would cost more than it saves. In region-based collectors, objects that span half a region or more are *humongous* and are allocated directly into old-generation regions. - **Promotion failure.** If the old generation cannot accept the promotions, the collection fails and escalates — typically to a full, compacting collection, which is the expensive event the whole design exists to avoid. ## Why the ladder exists at all Why not promote survivors immediately, and drop the survivor spaces? Because a single young collection is a very poor test of longevity. An object that happens to be alive at the instant a collection fires may still be a short-lived temporary — it merely lost the timing lottery. The survivor spaces impose a *repeated* test: only data that stays reachable across several collections is judged genuinely long-lived and moved to the region that is expensive to collect. The survivor spaces are, in effect, a filter that keeps garbage out of the old generation. ## The observable consequences This mechanism explains most everyday GC behaviour. Allocating enormous numbers of short-lived objects is cheap, because they simply never get copied. Holding a moderate amount of medium-lived data is expensive, because it is copied repeatedly between survivor spaces before being promoted. And a burst of allocation that outruns the survivor space quietly pushes short-lived garbage into the old generation, where it can only be cleaned up by a much more expensive collection.

  • Why does a young collection copy live objects rather than free the dead ones in place?
    Because in a young generation the overwhelming majority of objects are dead, so the live set is tiny. A copying collector touches only the live data and then frees the entire region in one operation, making its cost proportional to survivors rather than to heap size. It also compacts as a side effect, so allocation afterwards is a cheap pointer bump with no fragmentation.
  • Why are there two survivor spaces instead of one?
    A copying collector needs an empty destination to evacuate into, so one survivor space must always be free at the start of a collection. The collector copies eden's survivors plus the occupants of the in-use survivor space into the empty one, then swaps their roles. With a single survivor space there would be nowhere to compact the previous survivors into.

Survivor spaces are a probation period. One young collection is like passing a single day on the job — not proof of anything. Only after repeatedly showing up does an object get the permanent contract (promotion) into the region that is costly to clean.

saying these in an interview costs you the question

  • Saying objects are 'moved from eden to survivor when eden is full' as if eden fullness alone moves data — it is the collection that copies, and only live objects move.
  • Claiming dead objects are individually freed or their memory zeroed one by one during a young collection.
  • Believing every survivor is promoted after exactly one collection, ignoring survivor-space aging.
  • Assuming the tenuring threshold is a fixed constant that the JVM never varies at runtime.
  • Thinking large objects always start in eden.

context

open as a page

In a HotSpot JVM with a generational heap, what are eden, the survivor spaces and the old generation, and what does each one hold?

level: juniorimportance: must knowfreq 58%

basics

~20 s

The heap is shared by all threads and split into a young generation and an old generation. Young holds eden, where new objects are created, plus two survivor spaces holding objects that survived recent collections. Old holds objects that survived long enough to be promoted.

open as a page

In the HotSpot JVM, what data is stored in the metaspace region, and why does the JVM keep that data in native memory rather than inside the Java heap?

level: juniorimportance: must knowfreq 62%

basics

~20 s

Metaspace holds per-class runtime metadata: type structures, method bytecode, runtime constant pools, field and method descriptors, dispatch tables. It lives in native OS memory, so it grows on demand instead of competing for a fixed slice of the heap.

open as a page

In the JVM, what is a thread stack, and what is pushed onto it while a method executes?

level: juniorimportance: must knowfreq 60%

basics

~20 s

Every JVM thread gets its own stack, created with the thread. Each method invocation pushes one frame holding that call's local variables and working values; returning pops it. Stacks are private to their thread and disappear when the thread ends.

open as a page

What is a thread-local allocation buffer (TLAB) in the HotSpot JVM, and why does having one make object allocation cheap?

level: middleimportance: must knowfreq 60%

basics

~20 s

A TLAB is a private chunk of the young space handed to one thread. Inside it the thread allocates by advancing a pointer, with no lock and no atomic instruction. Only buffer refills and oversized objects touch the shared heap.

open as a page

A young-generation collection must not miss an object whose only reference comes from the old generation, yet it does not scan the old generation. What runtime structure makes that possible, what does the JVM do on every reference-field write to maintain it, and how does a region-based collector's version differ from a simple card table?

level: middleimportance: must knowfreq 52%

basics

~30 s

Cross-generational references are recorded. A write barrier runs on every reference store and marks the containing card (a small fixed-size chunk of the heap, 512 bytes in HotSpot) dirty in a card table. A young collection then treats objects in dirty cards as extra roots, scanning only those cards instead of the whole old generation. Region collectors keep per-region remembered sets of incoming references instead of one global table.

open as a page

HotSpot replaced the permanent generation with metaspace in Java 8. What concretely changed, and which data moved out of the permanent generation before that, in Java 7?

level: middleimportance: must knowfreq 56%

basics

~20 s

Java 8 removed the fixed-size permanent generation inside the heap and moved class metadata to native memory (metaspace), which grows on demand. Earlier, Java 7 had already moved the interned-string pool and static field values out of PermGen onto the normal heap.

open as a page

Name the categories of GC roots a JVM collector enumerates before tracing, and give an example of an object kept alive by each.

level: middleimportance: must knowfreq 52%

basics

~20 s

Local variables and operand-stack slots in active frames of every thread; static fields of loaded classes; JNI local and global references; live Thread objects themselves; objects currently used as monitors; plus JVM-internal references such as class loaders and resolved constant-pool entries.

open as a page

What does a single JVM stack frame contain, and at what point are the sizes of its parts decided?

level: middleimportance: must knowfreq 55%

basics

~20 s

A frame holds a local variable array, an operand stack, and a reference to the run-time constant pool of the method's class, plus implementation bookkeeping such as the return address. The two sizes are fixed at compile time and stored in the method's Code attribute, so the frame's size is known before it is pushed.

open as a page

Java gives you no free() for a direct byte buffer. How is that native memory actually released, what bounds how much of it a JVM may hold, and what happens when that bound is reached?

level: seniorimportance: must knowfreq 46%

basics

~20 s

Each direct buffer registers a cleanup action that frees its native block only after the buffer object becomes unreachable and the collector processes it. The total is bounded by -XX:MaxDirectMemorySize, defaulting to about the maximum heap; exceeding it throws OutOfMemoryError: Direct buffer memory.

open as a page

Developers often say allocating a small object on the JVM is cheaper than calling malloc in C. Mechanically, what does the JVM do on allocation that makes that plausible, and what does it pay for it later?

level: juniorimportance: should knowfreq 50%

basics

~20 s

Because a moving young collector leaves free space contiguous, allocation is just advancing a pointer and writing the object header, with no free-list search. The bill arrives later: the collector must find and copy the surviving objects.

open as a page

In the JVM's runtime data areas, what is the program counter (PC) register, and why does the specification give every thread its own instead of sharing one?

level: juniorimportance: should knowfreq 40%

basics

~20 s

The PC register holds the address of the JVM bytecode instruction a thread is currently executing. Each thread has its own because threads run independently and interleave, so every thread must be able to resume at its own instruction after being descheduled.

open as a page

How does a JVM decide the exact age at which a surviving object is promoted into the old generation? Explain both the configured maximum tenuring threshold and the threshold the collector computes at runtime, and how you would observe the actual age distribution.

level: middleimportance: should knowfreq 48%

basics

~20 s

An object is promoted when its age reaches the tenuring threshold. -XX:MaxTenuringThreshold sets the ceiling (default 15, the maximum the header age bits allow). Each collection the JVM also computes a lower effective threshold: it sums survivors by age until the survivor space's target occupancy is exceeded, and tenures from that age up. -Xlog:gc+age=trace prints the distribution.

open as a page

Some managed runtimes decide object liveness by reference counting. Why does the JVM use tracing from GC roots instead?

level: middleimportance: should knowfreq 42%

basics

~20 s

Reference counting cannot reclaim cycles: mutually referencing objects keep each other's counts above zero forever. It also charges every reference write with a count update, which must be atomic under concurrency. Tracing collects cycles naturally and costs proportional to live data.

open as a page

How do a frame's local variable array and its operand stack cooperate while bytecode executes, and how are arguments and return values handed between a caller's frame and a callee's frame?

level: middleimportance: should knowfreq 45%

basics

~20 s

Locals are addressable storage; the operand stack is the working area. Bytecode loads locals onto the operand stack, instructions pop operands and push results, and stores put results back. To call a method, the caller pushes receiver and arguments onto its operand stack; the invoke pops them into the callee's low local slots, and the callee's return value is pushed onto the caller's operand stack.

open as a page

A service allocates roughly 1 GB per second of mostly short-lived objects and its young space is 512 MB. How does allocation rate translate into collection frequency, and what levers change that?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Young collections happen roughly every time the young space fills: 1 GB/s into 512 MB is about two young collections per second. Levers are allocating fewer bytes, enlarging the young space, and reducing how much survives each cycle.

open as a page

A thread's private allocation buffer in the Java heap no longer has room for the next object. What choices does the HotSpot JVM make at that point, and what is meant by allocation-buffer waste?

level: seniorimportance: should knowfreq 30%

basics

~20 s

It either retires the buffer and grabs a fresh one, or allocates that one object in the shared young space. Retiring abandons the unused tail, which is waste; HotSpot only retires when the remaining tail is below a threshold, otherwise the big object goes outside the buffer.

open as a page

A JVM shows frequent young collections that each promote a large volume of data, and old-generation occupancy climbs over hours even though the application's steady-state live set is stable. How do you confirm this is premature promotion rather than a leak, and what causes it and fixes it?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Premature promotion means short-lived data is pushed into the old generation before it dies. Confirm it from GC logs: an age histogram with a collapsed tenuring threshold, large promoted bytes per young collection, and old-generation occupancy that drops back fully after an old/full collection — which distinguishes it from a leak, where occupancy after collection keeps rising.

open as a page

How is class-metadata memory allocated and released inside the JVM, and why can metaspace usage stay high even after an application stops using the classes involved?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Metadata is allocated in chunks from a per-class-loader arena. Nothing is freed class by class: an arena is released only when its class loader is unloaded during a collection. So one reachable loader keeps all of its classes' metadata committed.

open as a page

Which HotSpot flags bound class-metadata memory, and why does a JVM report a separate 'compressed class space' in addition to overall metaspace usage?

level: seniorimportance: should knowfreq 38%

basics

~20 s

-XX:MaxMetaspaceSize caps all class metadata. When compressed class pointers are enabled, type structures go into a separate contiguous native reservation, the compressed class space, sized by -XX:CompressedClassSpaceSize (1 GB reserved by default). Exhausting either raises an error.

open as a page

A tracing garbage collector in the JVM is free to relocate objects, but an operating-system read or write call needs a memory address that stays fixed for the whole duration of the call. How does the JVM bridge that gap when Java code hands a heap-allocated byte[] or a heap-backed java.nio.ByteBuffer to native code or to an NIO channel — what are the pinning and copying options, and what does each one cost?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Relocating collectors move objects, so a heap array has no stable address a system call can use. The JVM either pins it — JNI critical sections, which stall relocation — or copies it. NIO always copies, through a per-thread cached native buffer.

open as a page

When a JVM collector scans a thread's stack for GC roots, how does it know which stack slots hold object references rather than ints or floats, and why must that scanning happen at a safepoint?

level: seniorimportance: should knowfreq 28%

basics

~20 s

HotSpot is precise: the interpreter and the JIT publish oop maps saying which stack slots and registers hold references at specific code positions. Those maps are valid only at those positions, so threads must be paused exactly there — that is what a safepoint is.

open as a page

Every JVM stack frame holds a reference to the run-time constant pool of the class that declares the executing method. What is that reference used for while the method runs?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Bytecode names other classes, fields and methods symbolically by constant-pool index, not by address. The frame's constant-pool reference is how an executing instruction turns index #7 into a real target — resolving it on first use, loading classes as needed, and caching the result. This is the JVM's dynamic linking.

open as a page

With a fixed thread stack size, why does the same recursive Java method reach a different call depth from run to run, and typically a much greater depth once the JIT has compiled it?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Depth is stack bytes divided by frame size, and frame size is not constant. Interpreted frames are laid out generically from max_locals/max_stack; compiled frames are smaller and inlining can erase frames entirely. Add variable startup depth, differing entry stack usage and OS-level stack placement, and the achievable depth varies each run.

open as a page

A JVM thread has only one program counter register, yet Java methods call other Java methods and must resume correctly on return. Where is the caller's resume point kept during a nested call?

level: middleimportance: nice to knowfreq 22%

basics

~20 s

In the caller's stack frame. The single PC register always describes the current method only; on invocation the caller's frame retains its own resume position, and on return that frame becomes current again and execution continues after the invoke.

open as a page

The JVM specification states that a thread's program counter register value is undefined while that thread is executing a native (JNI) method. Why, and what does that imply for stack traces?

level: middleimportance: nice to knowfreq 26%

basics

~20 s

The PC register addresses JVM bytecode. Native code is machine code with no bytecode offsets, and its execution point is tracked by the hardware program counter, so the JVM promises nothing about the value until the thread returns to a Java frame.

open as a page

The JVM specification defines StackOverflowError or OutOfMemoryError conditions for most runtime data areas, but defines no error condition for the program counter register. Why is that consistent?

level: seniorimportance: nice to knowfreq 15%

basics

~20 s

Because it is fixed-size and allocated once per thread. It never grows with recursion or allocation, so it cannot be exhausted. The only adjacent failure is being unable to create a thread at all, reported as OutOfMemoryError on thread creation.

open as a page

The JVM specification describes a 'native method stack' alongside the Java virtual machine stack. What is that second stack for, and how does HotSpot actually arrange the two for a running thread?

level: seniorimportance: nice to knowfreq 28%

basics

~20 s

The Java stack holds frames for bytecode methods; the native method stack holds frames for methods written in C or other native languages, reached through JNI, which have no bytecode and no operand stack. The specification allows them to be separate; HotSpot interleaves Java and native frames on one OS thread stack sized by -Xss.

open as a page

How does HotSpot decide how large each thread's private allocation buffer should be, and when, if ever, would you override that decision with tuning flags?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

HotSpot sizes each buffer adaptively from that thread's recent allocation history and the young space available per thread, resizing at each collection. Overriding is rare; the real cases are pathological thread counts, tiny heaps, or workloads where logging shows excessive refills.

open as a page

The weak generational hypothesis has two halves: most objects die young, and references from older objects to younger ones are rare. Describe workloads where each half fails, what that costs at runtime, and how you would change heap or collector strategy in response.

level: principalimportance: nice to knowfreq 30%

basics

~20 s

When most data survives (large caches, in-memory stores, long batch working sets) copying collectors pay to move it repeatedly and the old generation dominates. When old objects are constantly rewritten to point at new ones, write-barrier and remembered-set costs dominate instead. Responses: size the young generation to the real lifetime distribution, reduce mutation of long-lived graphs, or choose a collector whose old-generation work is concurrent.

open as a page