Understanding Java Class Loading, Verification, Preparation, and Initialization
This article explains the Java Virtual Machine's class loading lifecycle—including loading, verification, preparation, resolution, and initialization—illustrated with multiple code examples that show when static blocks and fields are executed and why certain classes may not be initialized.
Many developers initially think that writing Java code is enough and that the JVM will load classes arbitrarily, but a solid grasp of the JVM's class‑loading mechanism is essential; the article begins with a sample program that defines SSClass , SuperClass , SubClass and a driver class, each containing static blocks and a static field.
The program's output is SSClassSuperClass init!123 , demonstrating that accessing a static field via a subclass triggers only the superclass's initialization; the subclass's static block is not executed because only the class that directly defines the static field is initialized.
The JVM loads a class through seven stages: Loading, Verification, Preparation, Resolution, Initialization, Using, and Unloading. Loading, Verification, and Preparation together form the linking phase.
Loading : the JVM obtains the binary byte stream of a class (from a .class file, network, or generated dynamically), converts it into runtime data structures in the method area, and creates a java.lang.Class object that serves as the entry point for the class's metadata.
Verification : the JVM checks that the class file conforms to the class‑file format, validates metadata against the Java language specification, performs bytecode control‑flow analysis, and verifies symbolic references.
Preparation : memory is allocated for static variables and they are given default values (zero for primitives). If a static field is declared static final with a constant value, that value is assigned during this phase; otherwise, explicit initializers are executed later in the initialization phase.
Resolution : symbolic references in the constant pool are replaced with direct references to other classes, fields, or methods.
Initialization : the JVM executes the class's <clinit> method, which is automatically generated from static blocks and static variable initializers, respecting the textual order in the source file. Static blocks can only reference variables declared earlier in the source.
Example code illustrating preparation and initialization order:
public class Test{ static { i=0; System.out.println(i); // illegal forward reference } static int i=1; }After correcting the forward reference, the program prints 1 because the static block sets i to 0, then the static field initializer sets it to 1, and finally main reads the value.
The article also discusses the difference between <clinit> and instance constructors ( <init> ), the guarantee that a superclass's <clinit> runs before a subclass's, and how interfaces generate <clinit> only when their static fields are accessed.
Thread‑safety is ensured: only one thread executes a class's <clinit> at a time; other threads block until initialization completes. A dead‑loop example shows how a long‑running static block can block other threads:
package jvm.classload; public class DealLoopTest{ static class DeadLoopClass{ static{ if(true){ System.out.println(Thread.currentThread()+"init DeadLoopClass"); while(true){} } } } public static void main(String[] args){ Runnable script = new Runnable(){ public void run(){ System.out.println(Thread.currentThread()+" start"); new DeadLoopClass(); System.out.println(Thread.currentThread()+" run over"); } }; new Thread(script).start(); new Thread(script).start(); } }According to the JVM specification, a class is initialized only in five situations (JDK 1.7): when new , getstatic , putstatic , or invokestatic bytecodes are executed; when reflection triggers class loading; when a subclass is initialized before its superclass; when the JVM starts the main class; and when a MethodHandle for a static field or method is resolved.
Additional examples show that creating an array of a class ( SuperClass[] arr = new SuperClass[10]; ) does not trigger initialization, and that accessing a compile‑time constant ( ConstClass.HELLOWORLD ) does not initialize the defining class because the constant is inlined.
The article concludes with a reference to the book “Deep Understanding of Java Virtual Machine” by Zhou Zhiming.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.