Class loading converts a binary representation into a Class object. Linking
and initialization are separate steps, thus loading a type does not imply that
its static initializer has run.
Loaders and Type Identity #
The JDK supplies bootstrap, platform, and application loaders. The bootstrap
loader defines core platform classes and is represented by null in selected
APIs; the platform loader defines platform modules; the application loader
loads application classes from the class path or module path. Custom loaders commonly use parent-first delegation:
- reuse an already loaded definition;
- ask the parent to load the binary name; then
- find bytes and call
defineClass.
Parent-first delegation protects platform type identity. It is a convention,
not an absolute rule: plugin systems sometimes use child-first loading with
strict package exclusions. A runtime type is identified by both its binary name
and its defining loader. Thus two loaders can define different
com.example.Plugin types, and instances cannot be cast between them.
Loading, Linking, and Initialization #
The JVM specification separates the lifecycle as follows:
- Loading obtains bytes, creates the
Classobject, and associates a defining loader. - Linking includes verification, preparation, and optional resolution. Verification checks structural and type-safety constraints. Preparation allocates static fields and assigns their default values. Resolution turns symbolic references into runtime references; an implementation may defer it.
- Initialization executes the synthesized
<clinit>method, which contains static field initializers and static blocks in source order.
Compile-time constants are assigned by preparation, so reading a constant variable can avoid initialization of its declaring class. Do not infer a portable resolution schedule from an implementation’s logging output.
Exact Initialization Triggers #
JLS §12.4.1 requires initialization immediately before the first active use of
a class: creating an instance, invoking a static method, assigning a static
field, or reading a static field that is not a constant variable. It also
includes a static assertion whose execution requires the class, selected
reflective operations, and specified method-handle behavior. Class.forName
initializes by default; use the three-argument overload with false to load
without requesting initialization.
Before class C initializes, its superclass initializes, followed where
applicable by superinterfaces that declare default methods in the order defined
by the JLS. Initializing a class does not initialize arbitrary interfaces it
implements. Reading Child.value initializes the class that actually declares
value, not necessarily Child.
Failure and Concurrency #
The JVM serializes initialization for each class or interface. Other threads
wait while one thread runs <clinit>; recursive requests by that same thread
are permitted. If initialization completes abruptly, the VM records the type
as erroneous, reports an initialization failure to the triggering use, and
later active uses fail with NoClassDefFoundError. Restarting the process or
using an independent loader is required to obtain another initialization
attempt.
Static initializers should establish simple, failure-free invariants. Network calls, lock acquisition, and configuration parsing can deadlock or permanently poison a type after one transient failure.
Comments
Read or comment on GitHub (sign-in required to comment)