skip to content

Initialization & Static Setup

Running a type's static initializer exactly once, lazily, on first active use — and knowing which accesses count as active use and which do not. Interviewers like the compile-time-constant trick and the thread-safe-but-deadlockable guarantee, because both reward a precise mental model rather than a memorized rule.

on this pageshow

questions

2

The Java compiler emits a synthetic method named `<clinit>` into a class file. What source constructs end up inside it and in what order, where does executing it sit relative to the JVM's loading, linking (verification, preparation, resolution) and initialization phases, and why does the JVM track "is initialized" per class-plus-defining-class-loader rather than per class name?

level: middleimportance: should knowfreq 42%

answer

  1. load → link (verify · prepare · resolve) → initialize
  2. <clinit> = static blocks + static field initializers, source order
  3. Preparation zeroes statics; no code runs there
  4. ConstantValue attribute: constants set at preparation, inlined into callers
  5. Runtime class = binary name + defining loader

basics

~20 s

The compiler merges all static blocks and static field initializers, in source order, into one synthetic <clinit> method. It runs in the initialization phase, after linking's preparation already zeroed those statics. A class's runtime identity is its name plus its defining loader, so each loader gets its own statics and its own <clinit> run.

solid answer

~50 s

`<clinit>` is a synthetic, static, no-arg, `void` method the **compiler** builds by concatenating every `static { ... }` block and every static field initializer expression **in source order**. If a class has neither, no `<clinit>` is emitted at all. The JVM's lifecycle is **load → link → initialize**. Loading finds the bytes and creates the runtime class. Linking is verification (bytecode is well-formed), **preparation** (static fields get their default values — `0`, `false`, `null` — with no user code executed), and resolution (symbolic references to the constant pool, which HotSpot does lazily). Only **initialization** executes `<clinit>`, so statics are never observably uninitialized memory: they hold defaults first, then `<clinit>`'s assignments. `static final` fields with compile-time constant values are special: the compiler stores a `ConstantValue` attribute, and preparation writes them — they never appear in `<clinit>`. The unit is the **runtime class** = (binary name, defining class loader). Two loaders defining the same bytes produce two distinct classes with two independent sets of statics.

code

java · 16 lines
java
public class Config {
    // compile-time constant -> ConstantValue attribute, written at PREPARATION,
    // and inlined into every class that reads it. Never appears in <clinit>.
    static final int MAX = 100;

    // static field initializer -> goes into <clinit>, in source order
    static Map<String, String> CACHE = new HashMap<>();

    // static block -> appended to <clinit> after the CACHE assignment
    static {
        CACHE.put("mode", System.getProperty("app.mode", "default"));
    }

    // not static: this goes into the CONSTRUCTORS, not <clinit>
    private final long created = System.nanoTime();
}

go deeper

for a junior

Core recall: the compiler bundles static blocks and static field initializers into one hidden <clinit> method, and the JVM runs it in the initialization phase — after the class has been loaded and linked.

for a middle

Name the sub-steps of linking and place preparation's zero-defaults before <clinit>; mention source-order concatenation and that a class with nothing static gets no <clinit> at all.

for a senior

Add the ConstantValue/constant-inlining path and the per-defining-loader identity rule, and connect them to real symptoms: stale constants after a partial recompile, duplicated static caches across module loaders, ClassCastException between same-named classes.

for a principal

Frame class-granular, loader-scoped initialization as an isolation primitive: it is what lets a platform host multiple library versions side by side, and it is why static singletons are a poor unit of process-wide state in any container, plugin or module-layer architecture.

## The three-phase lifecycle Before any of your code in a class can run, the JVM takes the type through three broad phases. **Loading** locates the class file bytes (usually via a class loader) and builds an internal runtime representation of the type, plus the `java.lang.Class` mirror object. Nothing you wrote executes here. **Linking** has three sub-steps. *Verification* proves the bytecode is structurally safe — the stack cannot underflow, types match, jumps land inside the method. *Preparation* allocates storage for static fields and writes each one's **default value**: `0` for numerics, `false` for `boolean`, `''` for `char`, `null` for references. This is a memset, not code execution. *Resolution* turns symbolic constant-pool references ("the field `CACHE` of class `Config`") into direct references; the specification allows this to be eager or lazy, and HotSpot resolves lazily, on first use of each entry. **Initialization** is the only phase that runs code you wrote, and the code it runs is exactly one method: `<clinit>`. The practical consequence of preparation preceding initialization is that a static field always holds *some* legal value. Code that observes a static during initialization (say, a static method called re-entrantly from within `<clinit>`) sees a default, never garbage. ## What the compiler puts into `<clinit>` `<clinit>` is *synthesized*, not written. The compiler walks the class body and appends, in **textual source order**: 1. the body of every `static { ... }` block, and 2. the assignment implied by every static field with an initializer expression (`static Map<String,String> CACHE = new HashMap<>();`). They interleave by position, which is why a static block placed above a field initializer can only see that field's default value while a block placed below it sees the assigned value. The resulting method is `static`, takes no arguments, returns `void`, and its name `<clinit>` is deliberately not a legal Java identifier — you cannot call it, override it, or inherit it. Only the JVM invokes it, and only once per runtime class. If a class declares no static blocks and no static initializers that require code, the compiler emits no `<clinit>` and initialization has nothing to execute for that type. Interfaces follow the same rule: an interface with initialized static fields (or, since Java 8, a `static` method plus initialized fields) gets its own `<clinit>`. ## Compile-time constants bypass `<clinit>` A `static final` field of primitive or `String` type whose initializer is a compile-time constant expression is stored in the class file as a `ConstantValue` attribute on the field. Preparation writes it directly. Such a field never appears in `<clinit>` — and, correspondingly, reading it from another class is not a use that requires the owning class to be initialized, because `javac` inlines the literal into the reading class's constant pool. Change the constant and recompile only the owner, and stale callers keep the old value; that is the classic "constant inlining" recompilation hazard, and it falls directly out of this mechanism. ## Why the granularity is the class Initialization state is a per-class flag with a small state machine (`not initialized` → `being initialized` → `initialized` / `erroneous`), not per-field bookkeeping. There is exactly one method to run, so there is exactly one bit of state to track. This is why you cannot initialize "half" a class: touching any non-constant static forces the whole `<clinit>` to complete first, including the parts you did not need. It is also why a failure anywhere in `<clinit>` marks the entire class erroneous rather than leaving the successfully-assigned fields usable. Superclasses participate at the same granularity: initializing a class requires its **direct superclass** to be initialized first (recursively up the chain), so the parent's `<clinit>` completes before the child's begins. Superinterfaces are only initialized if they declare default methods. ## Why the unit is per defining class loader A runtime class is identified by the pair **(binary name, defining class loader)** — the loader that actually called `defineClass`, not the one that was merely asked to load it. Two loaders that each define `com.acme.Config` from byte-identical files produce two *different* runtime types: not assignable to each other, with two separate copies of every static field and two separate `<clinit>` executions. That design exists so containers, application servers, plugin systems and module layers can host multiple versions of the same library without them colliding through static state. The flip side is the classic surprise: "my static cache/singleton exists twice" in an app server, or a `ClassCastException` between two objects whose classes print the same name. Hot-reload tooling relies on the same property — a redefined class is a new runtime class, so its `<clinit>` runs again. ## What to take away `<clinit>` is a compiler artifact, initialization is a JVM phase, and the two meet exactly once per runtime class. Preparation guarantees defaults before any code; source order governs what `<clinit>` does; constants skip the mechanism entirely; and "the class" for all of this means name *and* loader.

  • If preparation already sets a static `int` to 0, why does `static int n = 0;` still generate a `putstatic` in `<clinit>`?
    Because `n` is not `final`, so it carries no `ConstantValue` attribute and the compiler treats the `= 0` as an ordinary initializer expression to be executed. The redundant store is harmless — the JIT typically eliminates it — but at the class-file level the compiler does not reason about whether your value matches the default. Make the field `static final int n = 0;` and it becomes a compile-time constant handled entirely by preparation instead.
  • Two modules in an application server each hold a `static` counter in the same `com.acme.Metrics` class, and the counts do not agree. What is the mechanism?
    Each module was loaded by a different class loader, and each loader defined its own copy of `com.acme.Metrics`. A runtime class is identified by (binary name, defining loader), so those are two distinct types with two independent sets of static fields and two independent `<clinit>` runs. Static state is per runtime class, never per class name, so it is not global across loaders. Confirm it by comparing `Metrics.class.getClassLoader()` identities from each side.
  • Does `<clinit>` run for a class whose only static member is `public static final String NAME = "acme";`?
    No `<clinit>` is generated for that member at all. A `static final` field of `String` or primitive type with a constant expression initializer is emitted as a `ConstantValue` attribute and written during preparation, so there is no code to run. Callers also get the literal inlined at their own compile time, which means reading it does not even require the owning class to be initialized.

Preparation hands out a blank form with every box pre-printed as 0 or empty; initialization is the one pass of the pen that fills the boxes in. And each class loader gets its own copy of the form, even if the printed template is identical.

saying these in an interview costs you the question

  • Saying preparation runs the static initializers — preparation only writes default values; `<clinit>` runs later, in the initialization phase
  • Claiming instance initializer blocks and instance field initializers also go into `<clinit>` — those are copied into the constructors instead
  • Thinking `<clinit>` is a method you can call, override or inherit; only the JVM invokes it, and it is never inherited by subclasses
  • Assuming a `static final int` constant is assigned by `<clinit>` and that reading it forces the owning class to initialize
  • Treating static state as process-global — identity is (binary name, defining class loader), so two loaders mean two sets of statics

context

open as a page

The JVM guarantees a class's static initializer runs exactly once even when many threads touch the class at the same instant. How is that guarantee implemented, and what failure mode does the very same mechanism create in production?

level: seniorimportance: should knowfreq 34%

basics

~20 s

Each class has an initialization lock and a state (uninitialized, in progress by thread T, initialized, erroneous). One thread runs the initializer while others block; recursive entry by the same thread is allowed and proceeds. Two classes initializing each other from two threads therefore deadlock, and slow initializers stall every caller.

open as a page