jvm-rs/docs/class-loading.md

3.8 KiB

Class Loading and Boot Image

Location: crates/core/src/class_loader.rs, crates/core/src/bimage.rs

Boot Image (Bimage)

A 7z archive containing precompiled Java standard library classes.

Structure

pub struct Bimage {
    image: ArchiveReader<File>,       // 7z archive reader
    modules: Vec<String>,              // Available modules
    packages: HashMap<String, String>, // Package -> Module mapping
    pub total_access_time: Duration,   // Performance tracking
}
  • Default Location: ./lib/modules
  • Format: 7z compressed archive
  • Structure: <module>/classes/<class>.class
  • Default Module: java.base (used when no module is specified)

Class Loading Flow

The ClassLoader manages class resolution with a two-tier fallback mechanism:

Process

  1. Check Cache: Look in DashMap<(String, LoaderId), Arc<RuntimeClass>> for already-loaded classes
  2. Try Bimage: Attempt to load from boot image via bimage.get_class(module, class_fqn)
  3. Fallback to Disk: If not in bimage, load from filesystem at {CLASSPATH}/{class_name}.class
  4. Parse & Cache: Parse ClassFile using deku, create RuntimeClass, store in cache

Key Method

pub fn load_class(&mut self, what: &str, loader: LoaderId) -> Result<Arc<RuntimeClass>, VmError> {
    let bytes = self.bimage
        .and_then(|b| b.get_class("", what).ok())
        .or_else(|_| Self::load_class_from_disk(what))
        .map_err(|_| VmError::LoaderError(...))?;

    let (_, cf) = ClassFile::from_bytes(bytes.as_ref())?;
    let runtime = self.runtime_class(cf);

    // Store with loader ID for multi-loader support
    self.classes.insert((class_fqn, loader), Arc::new(runtime));
}

Classpath Handling

Resolution Priority

  1. Bimage (boot image) - Primary source for standard library
  2. Command-line argument (arg[1]) - User-provided classpath
  3. Default ./data directory - Fallback location

Implementation

fn load_class_from_disk(what: &str) -> Result<Vec<u8>, String> {
    let class_path = std::env::args()
        .nth(1)
        .unwrap_or("./data".to_string())
        .replace("\\", "/");

    let path = format!("{class_path}/{what}.class");
    // Load file from disk
}

Bootstrap Process

Location: crates/core/src/vm.rs - boot_strap() method

Steps

  1. Create VM: ClassLoader::with_bimage() - initializes with boot image
  2. Load Core Classes: Preloads essential VM classes
  3. Create Primitive Classes: Synthetic class objects for primitive types
  4. Initialize Classes: Run static initializers (<clinit>)

Core Classes Loaded

let classes = vec![
    "java/lang/String",
    "java/lang/System",
    "java/lang/Class",
    "java/lang/Object",
    "java/lang/Thread",
    "java/lang/ThreadGroup",
    "java/lang/Module",
    "java/lang/reflect/Method",
    // ...
];

RuntimeClass

Runtime representation of a loaded class:

Cached Data

  • Superclass Chain: super_classes: Vec<Arc<RuntimeClass>>
  • Interface Hierarchy: super_interfaces: Vec<Arc<RuntimeClass>>
  • Component Type: For array classes, reference to element type
  • Initialization State: Thread-safe InitState enum

Initialization States

pub enum InitState {
    NotInitialized,
    Initializing(ThreadId),  // Track which thread is initializing
    Initialized,
}

Method/Field Resolution

  • find_method() - Searches class then walks up superclass chain
  • find_field() - Same recursive behavior for fields
  • is_assignable_into() - Checks type compatibility with array covariance

Multi-Loader Support

Classes are keyed by (class_name, LoaderId) tuple to support:

  • Different class loaders loading same-named classes
  • Isolation between class loader namespaces
  • Proper class identity checks