JVM
Every JVM language ultimately runs here, so senior interviews drift away from syntax and toward the runtime underneath: how bytecode is interpreted and then JIT-compiled, where memory actually lives, how the collector reclaims it, and how classes are found and linked. This is the language-neutral half of a Java, Kotlin, or Scala interview.
on this pageshowhide
explore
- JVM Architecture & Execution14 questions
- JVM Architecture Overview4 questions
- Bytecode & Interpretation5 questions
- AOT Compilation & Native Image5 questions
- Runtime Memory Areas30 questions
- Heap & Generational Layout1 questions
- Thread Stacks & Stack Frames6 questions
- Class-Metadata Area (Metaspace)4 questions
- Program Counter Register4 questions
- Direct / Off-Heap Memory2 questions
- Reachability & GC Roots3 questions
- Object Allocation & TLAB5 questions
- Generational Hypothesis & Tenuring5 questions
- Garbage Collection44 questions
- Tri-Color Marking & Write Barriers4 questions
- Stop-the-World, Safepoints & GC Triggers6 questions
- Serial Collector5 questions
- Parallel Collector5 questions
- Concurrent Mark-Sweep Collector (Historical)4 questions
- Region-Based (Garbage-First) Collector5 questions
- Low-Latency Colored-Pointer Collector (ZGC)6 questions
- Class Loading & Initialization33 questions
- Loading Phase5 questions
- Linking: Verify, Prepare, Resolve5 questions
- Initialization & Static Setup2 questions
- ClassLoader Hierarchy4 questions
- Parent Delegation Model5 questions
- Custom ClassLoaders3 questions
- Class Identity & Uniqueness5 questions
- Load-Time Failures & LinkageError4 questions
- JIT & Adaptive Optimization25 questions
- Compilation Tiers (Tiered JIT)4 questions
- Hot-Code Detection & On-Stack Replacement5 questions
- Method Inlining & Devirtualization5 questions
- Classic Compiler Optimizations4 questions
- Speculative Optimization & Deoptimization5 questions
- Benchmarking Implications2 questions
- Memory Model20 questions
- Happens-Before Relationship3 questions
- Instruction Reordering & Memory Barriers3 questions
- Atomicity & Word Tearing4 questions
- Safe Publication1 questions
- Immutable/Frozen Field Semantics4 questions
- VM Runtime Tuning19 questions
- Heap Sizing5 questions
- Generational Layout Tuning3 questions
- Collector Selection6 questions
- GC Log Analysis5 questions
questions
185 · 7 sectionsWhat is the bytecode that a Java compiler emits into .class files, and why does the JVM execute that form instead of executing source text directly or compiling straight to native machine code?
basics
~20 sBytecode is a compact, platform-neutral instruction set for an abstract machine, stored in .class files. The compiler targets that abstract machine instead of a real CPU, so one compiled artifact runs on any JVM, and the JVM can verify it before running it.
Compiling a Java application ahead of time into a standalone native executable is an alternative to running it on the JVM with just-in-time compilation. What do you gain and what do you give up?
basics
~20 sYou gain millisecond startup, immediate peak-ish performance, and a much smaller memory footprint with no compiler or class loading at runtime. You give up profile-guided peak throughput, runtime dynamism such as unconstrained reflection, and fast builds.
Trace, in order, what a Java Virtual Machine does from the moment running code first references a type until that type's methods are executing as JIT-compiled native code — and name which JVM subsystem owns each step.
basics
~20 sOrder: load, verify, prepare, resolve, initialize, interpret, profile, compile. The class loader subsystem owns the first five (loading through running the static initializer); the execution engine owns the last three (interpreting bytecode, gathering profile counters, and JIT-compiling hot methods into native code).
Describe the three top-level subsystems that make up a Java Virtual Machine implementation and what each one is responsible for.
basics
~20 sClass loader subsystem: finds class files and loads, links (verify, prepare, resolve) and initializes types. Runtime data areas: heap, per-thread stacks and program counters, class-metadata area, code cache. Execution engine: interpreter, JIT compiler and garbage collector, which actually run and maintain the code.
Interpreting bytecode instruction by instruction is portable but slow. Mechanically, where does the slowness come from, and why does a runtime that interprets first still keep an interpreter around after it can compile code to native instructions?
basics
~20 sEach bytecode costs decode plus an indirect dispatch on top of its actual work, values shuttle through the operand stack instead of registers, and nothing is optimized across instruction boundaries. The interpreter is kept because it starts instantly, costs no compile time for cold code, and is the fallback when compiled code must be abandoned.
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?
basics
~20 sIt 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.
In a HotSpot JVM with a generational heap, what are eden, the survivor spaces and the old generation, and what does each one hold?
basics
~20 sThe 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.
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?
basics
~20 sMetaspace 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.
In the JVM, what is a thread stack, and what is pushed onto it while a method executes?
basics
~20 sEvery 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.
What is a thread-local allocation buffer (TLAB) in the HotSpot JVM, and why does having one make object allocation cheap?
basics
~20 sA 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.
Describe how a mark-sweep garbage collection algorithm works, phase by phase, and explain the characteristic problem it leaves behind in the heap.
basics
~20 sMark: trace from the roots and flag every reachable object. Sweep: walk the heap and return every unflagged object's space to a free list. Nothing moves, so surviving objects stay put and the reclaimed gaps between them leave the heap fragmented.
What is the Serial garbage collector in the HotSpot JVM, how does it collect the young and the old generation, and how do you turn it on?
basics
~20 sHotSpot's simplest collector: one GC thread, and every collection stops all application threads. The young generation is collected by copying live objects into a survivor space; the old generation by a mark-compact pass. Enable it with -XX:+UseSerialGC.
What does a 'stop-the-world' pause mean inside a JVM, and why does a garbage collector need application threads suspended at all?
basics
~20 sA stop-the-world pause is an interval where the JVM suspends every application thread so the runtime can do work that needs a stable view of the heap and of thread state — for example scanning thread stacks for references. Application code makes no progress for the duration.
Concurrent garbage collectors describe objects as white, gray, or black while tracing. Define each colour, and explain what the collector has established once no gray objects remain.
basics
~20 sWhite means not yet reached, gray means reached but its outgoing references not yet scanned, black means reached and fully scanned. When no gray objects remain, tracing is complete: everything reachable is black and every remaining white object is unreachable and can be reclaimed.
The Garbage-First collector is named for the order in which it reclaims memory. Explain what 'garbage first' means in practice and how the collector chooses which heap regions go into the next collection set.
basics
~20 sG1 tracks how much live data each region holds. For a given pause it collects all young regions plus, in mixed collections, the old regions with the least live data - the most garbage - because evacuation cost scales with live bytes, so those regions give the most free space per millisecond spent.
Which class loaders does a modern JVM create at startup, what does each of them load, and how are they linked to one another?
basics
~20 sThree built-in loaders: the bootstrap loader (native, no parent) defines the core runtime classes; the platform loader defines the remaining JDK modules; the application (system) loader defines your code from the class path and module path. They form a chain: application -> platform -> bootstrap.
When running code references a class for the first time, the JVM has to load it. Concretely, what happens during that loading step — where do the class's bytes come from, and what does the JVM end up with in memory?
basics
~20 sA class loader is asked for a binary name and produces the class's bytes (usually a .class file on the classpath, but any source works). The JVM parses that byte stream, loads the superclass and superinterfaces first, and creates a runtime type: internal class metadata plus a java.lang.Class object on the heap.
Walk through, step by step, what a standard Java class loader does when it is asked to load a type it has not loaded before. Which loader ends up actually defining a core JDK type such as java.util.List?
basics
~20 sIt first checks whether it has already loaded that name; if not, it asks its parent, which asks its own parent, all the way up to the bootstrap loader. Only if every ancestor fails does it try to find the bytes itself. Core JDK types are therefore always defined by the bootstrap loader.
Inside one running JVM, is a fully-qualified name such as com.acme.Config enough to identify a type uniquely? Explain what actually determines a class's runtime identity.
basics
~20 sNo. A runtime type is identified by the pair (fully-qualified name, defining class loader). Load the same class file with two different loaders and you get two distinct, non-interchangeable types, each with its own Class object.
How would you implement a class loader that loads class bytes from a non-standard source such as a database row or an encrypted archive, and which methods of java.lang.ClassLoader do you override?
basics
~20 sExtend java.lang.ClassLoader, override findClass(name): fetch the bytes yourself, then call the protected defineClass(name, bytes, 0, len), which asks the VM to parse them into a runtime Class. Leave loadClass alone so parent delegation still works.
Java is often described as both compiled and interpreted. After a class is loaded, what actually executes its methods, and how does that change while the program keeps running?
basics
~20 sSource is compiled ahead of time into bytecode. At runtime the JVM first interprets that bytecode, counts how often each method runs, and once a method is hot a just-in-time compiler turns it into native machine code used from then on. Both modes run side by side.
A Java service handles the same kind of request over and over. The first few hundred requests are visibly slower than requests handled a minute later, with no change to code, data or load. What is the JVM doing under the hood, and how does it decide which code deserves the speed-up?
basics
~20 sThe JVM first interprets bytecode while counting execution. Each method has counters for how often it is called and how often its loops iterate; when a counter crosses a threshold the JVM compiles that method to optimized native code, so hot paths speed up after warm-up.
HotSpot ships two just-in-time compilers, historically called the client (C1) and server (C2) compilers. How do they differ in design goals and output, and why does a single JVM contain both?
basics
~20 sC1 compiles fast and optimizes lightly, so code becomes native quickly with modest peak speed. C2 optimizes aggressively and compiles slowly, giving the best steady-state throughput. A single JVM uses both: C1 for quick warmup and profiling, C2 for the hottest methods.
What does it mean for a JIT compiler to compile a method speculatively, and what does the JVM do at runtime when one of those speculative assumptions turns out to be wrong?
basics
~20 sThe JIT compiles using the profile gathered so far — e.g. only one receiver type seen, this branch never taken — and replaces the unproven path with an uncommon trap. If the assumption breaks, the trap fires, the runtime deoptimizes that frame back to the interpreter at the same bytecode, and the method is later recompiled without the failed assumption.
HotSpot decides which bytecode to compile using two per-method counters. Name them, explain why one is not enough, and describe what the runtime does when a counter crosses its threshold.
basics
~20 sAn invocation counter (incremented on method entry) and a back-edge counter (incremented on every backward branch, i.e. loop iteration). Invocations alone miss long-running loops in rarely called methods. Crossing a threshold files an asynchronous compilation request; back-edge overflow requests on-stack replacement.
Which individual field and array-element reads and writes does the Java Language Specification guarantee to be atomic, and which ones does it explicitly leave non-atomic?
basics
~20 sReads and writes are atomic for object references and for every primitive type except long and double. Non-volatile long and double accesses may be split into two 32-bit halves, so a reader can see a mix of two values. Declaring them volatile makes them atomic again.
In the Java Memory Model, what special visibility guarantee applies to an instance field declared `final`, and what must the reading thread do to obtain it?
basics
~20 sFinal fields are frozen when the constructor returns. Any thread that later obtains the object's reference sees those fields correctly initialized, with no lock and no volatile on the reader's side, provided the reference did not escape during construction.
Which specific rules does the Java Language Specification list as creating a happens-before edge between two actions? Enumerate them.
basics
~20 sProgram order within a thread; unlocking a monitor before any later lock of it; a volatile write before any later read of that field; Thread.start before the new thread's actions; a thread's actions before a successful join; interrupt before the interrupt is detected; constructor end before the finalizer; default writes before any thread starts; plus transitivity.
In Java, the `volatile` keyword, `synchronized` blocks, `final` fields, and the `java.util.concurrent.atomic` classes (or `VarHandle` access modes) each grant a *different subset* of the guarantees the Java Memory Model specifies. For each of those four constructs, state precisely what the specification grants and what it withholds, and give a concrete bug that follows from picking the wrong one.
basics
~20 svolatile: visibility and ordering for that field plus single read/write atomicity, but no compound atomicity. synchronized: all three, only for threads taking the same lock. final: a one-time publication freeze at constructor end, nothing else. Atomics/VarHandle: single-variable atomicity, never multi-variable.
Why does the Java Language Specification define a memory model at all? What would be left unspecified about a multi-threaded Java program if it did not?
basics
~20 sBecause compilers and CPUs freely transform code, the language must state which value a read of a shared field may legally return. The memory model is that contract: it bounds optimization and reordering so one program behaves the same on every CPU.
A HotSpot JVM prints this garbage-collection log line: `[3.216s][info][gc] GC(7) Pause Young (Normal) (G1 Evacuation Pause) 512M->96M(1024M) 14.221ms`. Walk through what each field on that line means.
basics
~20 s[3.216s] is uptime, info the log level, gc the tag, GC(7) the collection's id. It was a young pause caused by G1 evacuation. Heap used went 512M before to 96M after, of 1024M total, taking 14.221 ms stop-the-world.
Which garbage collector does a modern HotSpot JVM use if you specify nothing, and what makes it pick a different one on its own?
basics
~20 sOn JDK 9 and later the default is G1 on any server-class machine (roughly two or more CPUs and enough memory). On a smaller machine or container, JVM ergonomics falls back to Serial. On JDK 8 the default was the Parallel collector.
What do the Java command-line options -Xms and -Xmx set, and what does the HotSpot JVM choose for them when you specify neither?
basics
~20 s-Xms is the initial heap size the JVM commits at startup; -Xmx is the maximum it may grow to. With neither set, HotSpot defaults to roughly 1/64 of available memory for initial and 1/4 for maximum, reading the container limit rather than host RAM when running under one.
How do you enable garbage-collection logging on a HotSpot JVM running JDK 9 or later, and which options control the log destination, the level of detail, and file rotation?
basics
~10 sUse the unified logging flag -Xlog. Its four colon-separated parts are selectors, output, decorators, output options: -Xlog:gc*:file=gc.log:time,uptime,level,tags:filecount=10,filesize=20M. It replaced -XX:+PrintGCDetails and -Xloggc, and is cheap enough to leave on in production.
Reading a HotSpot garbage-collection log, how do you tell a young (minor) collection from a mixed collection and from a full collection, and why is spotting a full collection important?
basics
~20 sYoung collections log Pause Young, G1's mixed ones Pause Young (Mixed) (young plus some old regions), and full ones Pause Full, which collects and compacts the whole heap in one long stop. Under G1 or a concurrent collector, a Full GC signals the concurrent machinery failed to keep up.