How it works
The explanation, with examples you can run.
Three steps
- Loading asks a class loader for the bytes of a class by name and turns them into a class inside the JVM. Its superclass and interfaces are loaded too.
- Linking starts with verification, which checks that the bytecode is type-safe and well formed, so a hand-crafted class file cannot corrupt memory. Preparation allocates static fields and sets them to default values (
0,false,null). Resolution turns symbolic references such as "methodprintlnof classPrintStream" into direct ones; the JVMS lets this happen lazily, the first time each instruction runs, and HotSpot does it that way. - Initialisation runs the class's static initialiser: the static field assignments and
static {}blocks, in source order.
When initialisation happens
The JLS lists the triggers: creating an instance, calling a static method, assigning a static field, reading a static field that is not a constant, some reflective calls, initialising a subclass, and being the main class. The JVM runs the initialiser exactly once and holds a lock while it does, so other threads wait. That gives a thread-safe lazy singleton for free:
public class Loaders { static class Config { static { System.out.println("Config initialised"); } static final String NAME = "app"; // a compile-time constant static String region = "eu"; // not a constant } public static void main(String[] args) { System.out.println(String.class.getClassLoader()); // null: bootstrap System.out.println(java.sql.Connection.class.getClassLoader()); // platform loader System.out.println(Loaders.class.getClassLoader()); // app loader System.out.println(Config.NAME); // copied in by javac: Config is not initialised System.out.println(Config.region); // first active use: the static block runs now }}Run it with java -Xlog:class+load Loaders (a HotSpot logging option) and you can watch Loaders$Config being loaded only at the last line.
The loader chain
Java 9 and later have three built-in loaders. The bootstrap loader is part of the JVM and loads core modules such as java.base; it has no Java object, so getClassLoader() returns null. The platform loader loads the rest of the Java SE and JDK modules, such as java.sql. The application (system) loader loads your class path and module path. Each loader asks its parent first and only searches itself if the parent can't find the class. That is why you can't replace java.lang.String by putting your own on the class path.
Frameworks and application servers create their own loaders, to load plugins or to keep two web apps apart. Then one rule matters: at run time a class is identified by its name and its defining loader. The same User.class loaded by two loaders gives two unrelated classes, and casting between them throws ClassCastException with the confusing message "User cannot be cast to User".
Start-up cost
Loading and verifying thousands of classes takes time. HotSpot's class data sharing (CDS) keeps an archive of already-parsed JDK classes that is mapped into memory at start-up; you saw it above if your log lines said source: shared objects file. You can build an archive that includes your own classes too.
Check yourself
3 questions. Get them all right to finish this topic.
0 of 3 right so far. Get all three to finish this topic.