skip to content
everything.jvm.classLoading

class loading

load → link → initialize. and the exact moments a class gets initialized.

chapter
chapter 13.05
reading time
8 min read

every class goes through three phases. loading: find the bytes and turn them into a class. linking: verify the bytecode, give static fields their zero values, and resolve names into real pointers (lazily). initialization: run <clinit> (your static fields and static blocks), exactly once, at the first active use.

loading can happen whenever the JVM likes. initialization happens at a moment the spec pins down precisely, and that's the part that bites.

the picture

you know how a static block sometimes runs way later than you'd expect, and sometimes not at all? that's because "the class is loaded" and "the class is initialized" are two different events, and only the second one runs your code.

think of a new hire. their paperwork arriving is loading. the background check is verification. getting a desk with empty drawers is preparation. learning where the printer is, the first time they need it, is resolution. and orientation (where they actually do something) only happens on the first day they're asked to work: that's initialization.

hover a stage.

hover a stage. the middle three together are linking. resolution can happen much later than the rest, one reference at a time.

watch a real run

here's a small program, run with -Xlog:class+load=info,class+init=info, which makes the JVM narrate exactly this. the log lines in the figure are copied from that run.

Shop.java
  1. 1class Base {
  2. 2 static { System.out.println("Base <clinit>"); }
  3. 3}
  4. 4
  5. 5class Price extends Base {
  6. 6 static int tax = 18;
  7. 7 static { System.out.println("Price <clinit>"); }
  8. 8}
  9. 9
  10. 10public class Shop {
  11. 11 public static void main(String[] args) {
  12. 12 System.out.println("main starts");
  13. 13 Price[] none = new Price[2];
  14. 14 System.out.println("made an array");
  15. 15 System.out.println("tax = " + Price.tax);
  16. 16 }
  17. 17}
classes
Shopno <clinit>
loadedlinkedinitialized
Base
loadedlinkedinitialized
Pricetax: doesn't exist yet
loadedlinkedinitialized
stdout
·
-Xlog:class+load,class+init (the program's classes only)
[0.017s] class,load Shop source: file:.../
[0.018s] class,init Start class verification for: Shop
[0.018s] class,init End class verification for: Shop
[0.018s] class,init 298 Initializing 'Shop'(no method)

before main can run, Shop is loaded, verified and initialized. "(no method)" means it has no static initializer to run. it's the 298th class initialized: the JDK did the other 297.

step 1/8
step through it. watch new Price[2] on line 13: both classes get loaded, and nothing else happens. the green phase only lights up when Price.tax is actually read.

two things that log proves. first, an array of Price loads Price without initializing it. second, the JVM verified Base and Price right before initializing them, not when they were loaded: HotSpot links lazily too.

the exact moments

the JVM spec (§5.5) lists what counts as a first active use. a class or interface is initialized just before the first:

new
creating an instance (new).
static fields
reading or writing a static field declared in that class (getstatic, putstatic). constants don't count, see below.
static calls
calling a static method (invokestatic).
handles
the first invocation of a method handle to one of the above. looking it up isn't enough.
reflection
Class.forName(name) (the 1-arg form), Method.invoke on a static method, Field.get on a static field.
subclasses
initializing a class initializes its superclass first (and superinterfaces that declare default methods).
main
the class whose main the launcher runs.

everything else (class literals, arrays of the type, casts, instanceof, reflection over the shape) may load the class, but won't initialize it. try your instincts:

1 of 150/0 right
new A();
does A get initialized?

answer, then see what the real program printed.

15 rounds. every output shown is what the real program printed on jdk 25. the tricky ones: A.CONST, B.counter, and the interfaces at the end.

preparation, then initialization

preparation and initialization are separate steps, and you can catch a class in between them. <clinit> runs the static initializers in source order, and anything that reads a static before its line has run sees the zero that preparation put there.

predict the output
java
class Counter {
    static Counter INSTANCE = new Counter();
    static int count = 10;
 
    Counter() { count++; }
}

what does Counter.count print?

Main.java
class Counter {
    static Counter INSTANCE = new Counter();
    static int count = 10;
 
    Counter() { count++; }
}
 
public class Main {
    public static void main(String[] args) {
        System.out.println(Counter.count);
    }
}
output
10

verification is a real gate

javac never produces code that fails verification, so you rarely meet the verifier. but anything that writes bytecode can get it wrong: a bytecode library with a bug, an agent, a hand-built class. here's a method that promises to return a String and returns an int instead, built with the Class-File API:

Main.java
void main() throws Throwable {
    // a method that promises to return a String, then returns the int 1
    byte[] bytes = ClassFile.of().build(ClassDesc.of("Bad"), cls -> cls
        .withMethodBody("name", MethodTypeDesc.of(ConstantDescs.CD_String),
            ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC,
            code -> code.iconst_1().areturn()));
 
    IO.println("built Bad, defining it...");
    try {
        MethodHandles.lookup().defineClass(bytes);
        IO.println("never printed");
    } catch (VerifyError e) {
        IO.println(e.getClass().getName() + ": " + e.getMessage().lines().findFirst().orElseThrow());
    }
}
output, from jdk 25
built Bad, defining it...
java.lang.VerifyError: Bad type on operand stack

this one needs a real JVM (reflection), so it doesn't run in the browser. copy it into java Main.java to try it.

the full message is worth reading once, because it's the verifier showing its work:

stderr
java.lang.VerifyError: Bad type on operand stack
Exception Details:
  Location:
    Bad.name()Ljava/lang/String; @1: areturn
  Reason:
    Type integer (current frame, stack[0]) is not assignable to reference type
  Current Frame:
    bci: @1
    flags: { }
    locals: { }
    stack: { integer }
  Bytecode:
    0000000: 04b0

04 b0 is the whole method: iconst_1, areturn. without the verifier, the JVM would hand the integer 1 to the caller as if it were a pointer to a String, and the first method call on it would crash the process, or worse, not crash.

the initialization lock

the JVM guarantees <clinit> runs once, even if ten threads hit the class at the same moment. it does that with a per-class initialization lock: the first thread runs <clinit>, everyone else waits. which means two classes whose static initializers need each other can deadlock:

Deadlock.java
public class Deadlock {
    static class Left {
        static final int VALUE;
        static {
            pause();
            VALUE = Right.VALUE + 1;
        }
    }
    static class Right {
        static final int VALUE;
        static {
            pause();
            VALUE = Left.VALUE + 1;
        }
    }
    static void pause() {
        try { Thread.sleep(100); } catch (InterruptedException e) {}
    }
    public static void main(String[] args) {
        System.out.println("pid " + ProcessHandle.current().pid());
        new Thread(() -> System.out.println(Left.VALUE), "t1").start();
        new Thread(() -> System.out.println(Right.VALUE), "t2").start();
    }
}

it prints its pid and then nothing, ever. here's jcmd <pid> Thread.print from the real run:

jcmd <pid> Thread.print
"t1" #25 [43011] prio=5 os_prio=31 cpu=0.34ms elapsed=2.23s ... in Object.wait()
   java.lang.Thread.State: RUNNABLE
	at Deadlock$Left.<clinit>(Deadlock.java:6)
	- waiting on the Class initialization monitor for Deadlock$Right
	at Deadlock.lambda$main$0(Deadlock.java:21)
 
"t2" #26 [42755] prio=5 os_prio=31 cpu=0.23ms elapsed=2.23s ... in Object.wait()
   java.lang.Thread.State: RUNNABLE
	at Deadlock$Right.<clinit>(Deadlock.java:13)
	- waiting on the Class initialization monitor for Deadlock$Left
	at Deadlock.lambda$main$1(Deadlock.java:22)
keylines
  • load, link (verify, prepare, resolve), initialize. only the last one runs your code.
  • initialization happens at the first new, static field use, static call, or subclass init. not earlier.
  • compile-time constants are copied into callers. changing one means recompiling everyone.
  • statics start at zero and get assigned in source order. read one too early and you see the zero.
  • <clinit> runs once, under a lock. two classes that need each other at init time can deadlock, silently.