Java Virtual Machine Class Loading Mechanism

Java Virtual Machine Class Loading Mechanism

  1. Class Loading Overview

The class loading mechanism in Java involves three main steps:

  1. Loading class files into memory through the class loading subsystem

  2. Creating corresponding runtime data structures in the method area

  3. Generating a java.lang.Class object in the heap to serve as an entry point for accessing the method area data structures

  4. Class Loading Process


2.1 Loading Steps

The loading process consists of three main steps:

  1. Obtaining the binary byte stream of a class using its fully qualified name and loading it into memory
  2. Converting the binary byte stream into runtime data structures in the method area
  3. Generating a java.lang.Class object in the heap as an entry point for accessing the method area data structures

2.2 Verification

Purpose: Ensure the byte stream of the class file meets the current virtual machine requirements.

2.2.1 File Format Verification

Based on the byte stream verification:

  • Checking if the file starts with the magic number 0xCAFEBABE
  • Verifying that the major and minor version numbers are within the current virtual machine's processing range
  • Ensuring constants in the constant pool don't include unsupported constant types (checking constant tag flags)
  • Validating that various index values pointing to constants don't reference non-existent constants or mismatched types
  • Cofnirming that CONSTANT_Utf8_info constants contain properly UTF-8 encoded data
  • Checking that all part of the class file and the file itself don't contain deleted or additional information

2.2.2 Metadata Verification

Based on the method area storage structure verification:

  • Verifying if the class has a parent class (all classes except java.lang.Object should have a parent)
  • Checking if the class inherits from a class that cannot be inherited (classes marked as final)
  • If the class is not abstract, ensuring it implements all required methods from its parent class or interfaces
  • Ensuring fields and methods in the class don't conflict with the parent class, for example:
    • Overriding final fields from the parent class
    • Having invalid method overloading (e.g., methods with identical parameters but different return types)

2.2.3 Bytecode Verification

This stage analyzes class methods to ensure they won't cause security issues during execution:

  • Ensuring data types on the operand stack match the instruction code sequence at all times
  • Guaranteeing that jump instructions don't target bytecode instructions outside the method body
  • Validating that type conversions in the method body are valid
  • Addressing the Halting Problem: While program verification can't be absolutely accurate, it can't determine if a program will terminate in finite time

2.2.4 Symbol Reference Verification

This involves matching information outside the class (various symbol references in the constant pool):

  • Verifying if fully qualified names of classes or interfaces in symbol references can find corresponding classes or interfaces
  • Checking if corresponding methods and fields can be found using field descriptors or method descriptors in specified classes
  • Ensuring that the accessibility (private, protected, public, default) of classes, fields, and methods in symbol references can be accessed by the current class

2.3 Preparation

  1. Allocating memory space for class variables in the method area
  2. Initializing class variables to default values (actual assignment happens during initialization)

2.3.1 Example

public class SampleApp {
    public static int counter = 999;
}

After the preparation phase, the initial value of counter is 0, not 999. The putstatic instruction that assigns 999 to counter is placed in the class constructor method after compilation.

2.3.2 Special Case

public class SampleApp {
    public static final int counter = 999;
}

In this case, the value of counter is 999 after the preparation phase.

2.4 Resolution

Converting symbolic references in types to direct references.

  • Resolving classes or interfaces, fields, and class or interface methods
  • Loading the class using a class loader, gradually resolving fields and methods during loading
  • Symbolic references are explicitly defined in the constant pool as literal forms
  • Direct references are pointers to targets or relative offsets

2.4.1 Class or Interface Resolution

  1. Determine if C is an array type
  2. If C is an array type:
    1. The JVM passes the N (fully qualified name of class C) to D (class loader for C) to load class C
    2. During loading, metadata and bytecode verification may trigger loading of related classes (parent classes or implemented interfaces). Any exceptions during this process cause resolution failure
  3. If C is not an array type:
    1. If the array element type is an object: load the array element type according to rule 1
    2. Otherwise: the virtual machine generates an array object representing the dimensions and elements of the array
  4. If no exceptions occur, verify if D has access permission to C. If not, throw java.lang.IllegalAccessError

2.4.2 Field Resolution

First, find the corresponding class or interface symbol reference in the class field table. If the symbol reference is unresolved, resolve it.

  1. If class C contains field F with the specified descriptor, return the direct reference to this field and end the search
  2. Otherwise, if C implements interfaces, recursively search from bottom to top through interfaces and their parent interfaces for field F. If found, return the direct reference and end the search
  3. Otherwise, if C is not Object, recursively search from bottom to top through its parent classes for field F. If found, return the direct reference and end the search
  4. Otherwise, the search fails, throw java.lang.NoSuchFieldError
  5. If the search successfully returns a reference, perform permission verification on the field. If access is not permitted, throw java.lang.IllegalAccessError

2.4.3 Class Method Resolution

Similar to field resolution, first resolve the class or interface symbol reference referenced by the class_index item in the class method table. If successful, denote this class as C.

  1. Class and interface method symbol references have separate constant type definitions. If C is an interface in the class method table, throw java.lang.IncompatibleClassChangeError
  2. If C contains method F with the specified descriptor, return the direct reference to this method and end the search
  3. Otherwise, recursively search from bottom to top through parent classes for method F. If found, return the direct reference and end the search
  4. Otherwise, recursively search through the interface list and their parent interfaces implemented by class C for method F. If a matching method is found, it indicates C is abstract, so throw java.lang.AbstractMethodError
  5. Otherwise, the method search fails, throw java.lang.NoSuchMethodError
  6. If the search successfully returns a direct reference, perform permission verification on the method. If access is not permitted, throw java.lang.IllegalAccessError

2.4.4 Interface Method Resolution

Similar to class method resolution, first resolve the class or interface symbol reference referenced by the class_index item in the interface method table. If successful, denote this interface as C.

  1. Unlike class method resolution, if C is a class rather than an interface in the interface method table, throw java.lang.IncompatibleClassChangeError

  2. Otherwise, search interface C for a method with a simple name and descriptor matching the target. If found, return the direct reference and end the search

  3. Otherwise, recursively search through parent interfaces of C up to Object. If a method matching the descriptor is found, return the direct reference and end the search

  4. Otherwise, the method search fails, throw java.lang.NoSuchMethodError

  5. Since all interface methods are public by default, there are no access permission issues, so interface method symbol resolution should not throw java.lang.IllegalAccessError

  6. Initialization


  1. The final step of the class loading process
  2. During preparation, variables have been assigned system-required initial values. During initialization, variables and other resource are initialized according to the programmer's plan.

3.1 Variable Definition Order in Static Blocks

The order collected by the compiler is determined by the order of statements in the source file. Static blocks can only access variables defined before them. Variables defined after can be assigned to but not accessed in earlier static blocks.

3.2 Initialization Method Execution Order

The virtual machine ensures that the parent class's initialization method is executed before the subclass's initialization method. Therefore, the first initialization method executed in the virtual machine is always java.lang.Object. This also means static blocks defined in the parent class take precedence over variable assignments in the subclass.

3.3 clinit Method

The clinit() method is not mandatory for classes or interfaces. If a class has no static blocks and no variable assignments, the compiler may not generate a clinit() method for it.

3.3.1 clinit Definition

  • Execution period: Class initialization phase (only called by JVM, responsible for class variable initialization)
  • Content: All class variable initialization statements and static initializers of the type

3.3.2 Interface clinit Method

Interfaces cannot use static blocks but still have variable initialization operations, so interfaces also generate clinit() methods like classes. However, unlike classes, interface initialization doesn't require executing the parent interface's initialization method first. The parent interface's initialization method is only executed when its variables are used. Additionally, implementing classes do not execute the interface's clinit() method during initialization.

3.3.3 clinit Locking

The virtual machine ensures that a class's clinit() method is properly locked and synchronized in a multi-threaded environment. If multiple threads simultaneously initialize a class, only one thread will execute the clinit() method while others wait until the active thread completes.

3.4 Initialization Trigger Conditions (Active References)

There are exactly four conditions that trigger class initialization:

  1. Creating object instances (new), getting class variable values (except for final values, which are placed in the constant pool during compilation), setting class variables (putstatic), and calling static methods (invokestatic)
  2. Using reflection methods from the java.lang.reflect package to invoke class methods
  3. When initializing a subclass, if its parent class hasn't been initialized yet, the parent class must be initialized first
  4. During virtual machine startup, the class containing the main method is initialized first

3.5 Initialization Trigger Conditions (Passive References)

Besides the four active reference conditions, other references to a class won't trigger its initialization:

  1. In a third-party class, using a subclass reference to access a parent class variable doesn't initialize the subclass
  2. In a third-party class, referencing a class through array definition doesn't initialize the class
  3. In a third-party class, referencing a class constant (variables marked as both final and static) doesn't trigger class initialization (because the constant is placed in the third-party class's constant pool after compilation)

Tags: JVM Class Loading bytecode verification class initialization Memory Management

Posted on Fri, 30 Oct 2026 16:01:42 +0000 by yogicasey