Class loading, linking, and initialization are the steps that turn class-file bytes into usable runtime class state inside a JVM process.
Learning Question
What does it mean for a class to be “loaded” by the JVM?
Developers often say a class is loaded when they really mean one of several different things. A class may be found as bytes, defined by a class loader, verified, prepared, resolved, or initialized.
The first mental model is:
A class becomes usable through phases. Loading finds and defines it, linking makes it safe and connected enough to use, and initialization runs its class initialization code.
Loading
Loading is the process of finding class-file bytes and creating a runtime class representation from them.
A class loader is responsible for locating or producing the byte sequence for a class name and asking the JVM to define the class. Common sources include directories, JAR files, modules, generated byte arrays, application servers, agents, and framework-specific loaders.
In the JVM, class identity is not only the class name. It is effectively the combination of:
binary class name + defining class loaderThis is why two classes with the same binary name can still be different runtime types if different class loaders define them. This behavior is central to plugins, containers, application servers, and some framework isolation mechanisms.
Linking
Linking prepares a loaded class for execution. It includes verification, preparation, and resolution.
Verification checks that the class file is structurally valid and safe for the JVM execution model. It prevents invalid bytecode from violating runtime assumptions such as stack correctness and type safety.
Preparation allocates and initializes storage for static fields to their default values. This is not the same as running Java static initializer code. For example, an int static field is prepared with 0 before class initialization assigns the source-level value.
Resolution converts symbolic references into more direct runtime references. For example, a constant-pool reference to a method can be resolved to the actual method metadata the JVM will use. JVM implementations may perform some resolution lazily, so it is better to think of resolution as a linking activity that can happen before or during use depending on the reference and implementation.
Initialization
Initialization is the phase where the class initialization method runs.
The compiler represents static field initializers and static blocks as class initialization code, often referred to as <clinit>.
For example:
class Config {
static int port = loadPort();
}The call to loadPort() is not performed during ordinary preparation. It runs during class initialization, before the class is first actively used according to JVM rules.
Initialization matters because it can execute arbitrary application code. It can read files, touch environment variables, connect to systems, throw exceptions, load other classes, or trigger deadlocks if initialization paths interact badly.
Loading Is Not Initialization
This distinction prevents many confusions.
A class can be loaded but not yet initialized. A class can be linked but not yet have run its static initialization code. Seeing a class in a diagnostic output does not always mean all of its application-level static behavior has run.
Likewise, a failure during initialization is different from a failure to find class bytes.
| Failure | Meaning |
|---|---|
ClassNotFoundException | Code asked to load a class by name, but it could not be found |
NoClassDefFoundError | A class that was expected at runtime could not be found or used |
NoSuchMethodError | A symbolic method reference resolved against an incompatible runtime class |
ExceptionInInitializerError | Class initialization code ran and failed |
Parent Delegation and Custom Loaders
Many Java environments use parent delegation: a class loader first gives its parent a chance to load a class before defining it itself.
This helps core platform classes remain stable and avoids accidental duplication. However, real systems can customize class loading. Application servers, plugin systems, test frameworks, build tools, and instrumentation agents may use class loader arrangements that are more complex than a single flat classpath.
For practical reasoning, the important question is:
Which class loader defined this class, and which class path or module path was visible to it?
Core Mental Model
Keep these boundaries separate:
- Loading finds class-file bytes and defines a runtime class representation.
- Linking verifies the class, prepares static storage, and resolves symbolic references as needed.
- Initialization runs static initialization code.
- A class’s runtime identity depends on its name and defining class loader.
- Class loading failures, linkage failures, and initialization failures point to different causes.
Final Summary
Class loading is not one single event called “the class appears.”
The JVM turns class-file bytes into usable runtime state through loading, linking, and initialization, and each phase has different rules, failures, and diagnostic meaning.