After the JVM has read a class file into the runtime, linking happens in three sub-phases before any of the class's own initializer code runs. Name them and describe what each one is responsible for.
answer
- Verify → Prepare → Resolve, then initialize
- Verify: stack discipline, type safety, StackMapTable
- Prepare: statics allocated, zero defaults, ConstantValue
- Resolve: symbolic → direct reference
- Spec allows lazy; HotSpot is lazy
basics
~20 sVerification checks that the bytecode is structurally and type-safe and that the operand stack is disciplined. Preparation allocates the static fields and sets them to default zero values, not to their programmed initializers. Resolution replaces symbolic references in the constant pool with direct references, and may be done lazily.
solid answer
~50 sLinking sits between loading and initialization and has three sub-phases defined by the JVM specification. **Verification** proves the class file is well-formed and the bytecode is type-safe: operand-stack depth and types agree at every merge point, jumps land on real instructions, locals are typed consistently, access rules hold, and an object is initialized before use. Malformed bytecode is rejected with a `VerifyError` rather than executed. **Preparation** allocates storage for the class's static fields and writes default values into them — zero, `false`, `null` — explicitly *not* the values written in the source, which are assigned later during initialization. Fields that are compile-time constants carry a `ConstantValue` attribute and get their value here. **Resolution** turns the constant pool's symbolic references — class names, name-and-type pairs — into direct references such as a field offset or a method entry. The specification permits eager or lazy resolution; HotSpot resolves lazily, on first execution of the referencing instruction.
go deeper
Recall the three names and one sentence each: check the bytecode, allocate statics with default values, turn names into real references.
Be precise that preparation writes zero defaults rather than the source initializers, and that resolution may be lazy.
Connect each phase to an observable symptom — a VerifyError from a bytecode agent, a static field seen as null, a NoSuchMethodError that appears only when a rare path runs.
Frame linking as the boundary that makes independently compiled artifacts safe to combine at run time, and reason about the cost and failure timing that lazy resolution shifts into production.
## Where linking sits A class goes through loading (reading the bytes and creating the runtime representation), then **linking**, then initialization (running static initializers). Linking is the phase that makes the class usable by the execution engine, and the specification splits it into verification, preparation and resolution — in that order, though implementations may interleave them with loading and may delay resolution arbitrarily. ## Verification: making untrusted bytecode safe The JVM executes bytecode from anywhere — compiled by any tool, generated at run time, or downloaded. Nothing about the source language can be assumed. Verification is the JVM's proof that the code cannot break the machine's invariants: it cannot pop from an empty operand stack, treat an integer as a reference, jump into the middle of an instruction, write a `String` into an `int` local, access a private member of another class, or use an object before its constructor has run. Modern class files carry a `StackMapTable` attribute, produced by the compiler, which states the types present at each branch target. The verifier then only has to *check* the claimed types rather than infer them by data-flow analysis, which is why verification is fast. If the check fails, the class is rejected with a `VerifyError`; execution of unsafe code never begins. ## Preparation: storage and defaults Preparation allocates memory for the class's static fields and gives each one its default value for its type: `0`, `0L`, `0.0`, `'\0'`, `false`, `null`. This matters because it defines what a static field holds *before* initialization runs — and a class can be observed in that state, for example by another class touched during a circular initialization. One exception: a static field that is a compile-time constant carries a `ConstantValue` attribute in the class file, and preparation assigns that value directly. Instance fields are not involved at all here; their storage belongs to objects and is zeroed at allocation. ## Resolution: symbols become addresses The class file never contains addresses. Every reference to another class, field or method is *symbolic*: a name in the constant pool, plus a descriptor. Resolution is the act of turning `Methodref -> java/util/List.add:(Ljava/lang/Object;)Z` into something the interpreter or compiled code can use directly — a vtable/itable index, a field offset, or a pointer to a method structure. Resolving a reference means locating the target class through the appropriate loader, applying the specification's field- or method-lookup algorithm (search the class, then superclasses, then superinterfaces, choosing the maximally specific method), and checking access. If the member is absent or inaccessible, resolution fails. The specification deliberately permits two strategies. *Eager* resolution links everything at link time — simple, but it forces loading of every referenced class. *Lazy* resolution defers each reference until the instruction that uses it first executes. HotSpot is lazy, which is why a class whose method references a missing type loads and runs happily until the branch that calls into that type is actually taken. ## Why the three-phase split is worth knowing It explains behaviour candidates otherwise find mysterious: static fields visibly holding zeros before initializers run; failures that appear only when a rare code path executes; classes that load fine and then reject a bytecode-rewriting agent's output. Each of those maps to exactly one sub-phase.
- Is the order of the three sub-phases strictly fixed?Verification and preparation precede any use of the class, and in that sense are ordered. Resolution is deliberately unconstrained: the specification allows an implementation to resolve every constant-pool entry at link time or to defer each one until first use. HotSpot defers, so resolution routinely happens long after the class has been initialized and is running.
- Where does initialization fit relative to linking, and what distinguishes it from preparation?Initialization follows linking and runs the class's static initializer code, assigning the values written in the source. Preparation only allocates the static fields and stamps default zero values into them. A static field observed between the two phases holds its default, not the programmed value.
saying these in an interview costs you the question
- Merging preparation and initialization — claiming static fields get their source values during preparation
- Believing resolution must complete before the class can be used
- Thinking verification checks program logic or business correctness rather than type and structural safety
- Placing verification after initialization