Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Learn › Java › JVM

Class loading and initialization

When does the static initializer of a class run?

A static initializer written in a class does not run when the program starts. It runs once, when the class is first "really" used. But not every appearance of the class name in the code counts as that kind of use.

Checked against real execution · The 18 combinations were newly compiled and run on JDK 21 and checked against the results

Concepts in this lab

Class loader
This JVM component finds class files and reads them into memory. Classes are not all read when the program starts; each class is loaded at the point when it is first needed. After a class is loaded, the JVM makes it ready for use through linking (verification, preparation, and resolution of symbolic references) and initialization (running static initializers).
Class initialization
This is the stage in which the static fields of a class are given their initial values and its static initializers are run. It is a different stage from loading, which reads the class in, and it happens at the moment the class is first actually used, for example when an object of the class is created, a static method is called, or a static field that is not a constant is used. It happens only once per class, and when a subclass is initialized, its superclass is initialized first. Creating an array, using ClassName.class, and reading a compile-time constant do not trigger initialization.
Static initializer
This is a block of code written inside a class in the form static { ... }, and it runs once when the class is initialized. It is used when the value to put into a static field has to be computed with several lines of code. The initializer expressions of static fields and the static initializers run from top to bottom, in the order in which they are written in the source. Unlike a constructor, which runs every time an object is created, it does not run again no matter how many objects are created.
Compile-time constant
This is a variable, such as static final int MAX = 10, that is final, has a primitive type or the type String, and gets its value from a constant expression such as a literal. The compiler puts the value directly into the places that use such a constant, so at run time the class that declares the constant is not looked up and is not initialized either. A field whose value comes from a method call is not a constant even if it is final, because its value cannot be known at compile time. Because the value is put in directly, when you change the value of a constant you must also recompile the code that uses it for the new value to take effect.
static
This keyword marks a member as belonging to the class, not to an instance. A static field exists only once per class and is shared by all instances, and a static method is called without creating an object. A static field whose value changes is effectively a global variable, so it easily causes problems when several threads access it at the same time.

Try these

Running with the default settings

  1. System.out.println("main starts"): The program has started. It has not yet reached any code that uses Parent or Child, so the JVM has neither loaded the two classes nor run a static initializer.
  2. int a = Parent.MAX: MAX is a constant whose value is fixed at compile time. The compiler has already turned this line into the same thing as int a = 10; so at run time there is no need to look up the Parent class. The class is not loaded, and its static initializer does not run.
  3. System.out.println("first use done"): Number of times the static initializers have run so far: Parent 0, Child 0.
  4. int b = Parent.MAX: Again the value 10 is used directly. No matter how many times the constant is read, the class is not initialized.
  5. System.out.println("second use done"): The program ends. Number of times the static initializers ran: Parent 0, Child 0.

Result of this run

Code

class Parent {
    static final int MAX = 10;                        // a constant whose value is fixed at compile time
    static { System.out.println("Parent init"); }    // static initializer
}

class Child extends Parent {
    static { System.out.println("Child init"); }
}

System.out.println("main starts");
int a = Parent.MAX;
System.out.println("first use done");
int b = Parent.MAX;
System.out.println("second use done");

Assumptions of this model

For each of the 18 combinations, source code identical to the code on the screen was newly compiled and run on JDK 21 on this PC, and the order of the printed lines and the classes that were loaded were checked against the model. The point at which each class was loaded was checked against the JVM log obtained with the -Xlog:class+load option. When initialization happens is defined by the Java Language Specification, so it is the same on every JVM. When a class is loaded, in contrast, is left to the JVM implementation, so "a class is loaded when it is first used" and "a class is not even loaded when only a constant is read" are behaviors confirmed on the JVM of this PC (HotSpot). That reading the constant is compiled into the value 10 was confirmed by looking at the bytecode with javap. The detailed steps that a class goes through after loading and before initialization, such as verification and preparation, and the initialization rules for Class.forName and for interfaces are not covered.

Previous lab: JIT warmup · Next lab: I/O streams and buffers

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these