Blog

Exploring the Java Virtual Machine (JVM): Architecture

The Java Virtual Machine (JVM) is an integral part of the Java Runtime Environment (JRE), enabling Java applications to run on any device or operating system. The architecture of the JVM plays a crucial role in this cross-platform compatibility, as well as in the performance and security of Java applications. Here’s an overview of the key components and how they work together within the JVM architecture:

1. Class Loader Subsystem

The Class Loader Subsystem in the Java Virtual Machine (JVM) is a crucial component responsible for loading class files into the JVM. It performs three primary activities: loading, linking, and initializing classes and interfaces. Here’s a detailed look at each of these steps:

Loading

In this phase, the Class Loader reads the .class files (compiled Java bytecode files) from the file system, network, or any other sources and loads them into the memory. The JVM uses three built-in class loaders:

  • Bootstrap Class Loader: It loads the core Java API classes present in the <JAVA_HOME>/jre/lib directory.
  • Extension Class Loader: It loads the classes that are an extension of the standard core Java classes, located in the <JAVA_HOME>/jre/lib/ext directory or any other directory specified by the java.ext.dirs system property.
  • System/Application Class Loader: It loads the classes from the system classpath, which includes class files from the directory or JAR files mentioned in the classpath environment variable or -classpath command-line option.

Class loaders use a delegation model for loading classes: when a class loader is asked to load a class, it delegates the request to its parent class loader before attempting to load the class itself. This model ensures that classes available in the parent class loader are not reloaded by the child class loader, maintaining consistency and preventing multiple versions of the same class from being loaded.

Linking

After a class or interface has been loaded into memory, it goes through the linking process, which involves:

  • Verification: Ensures the correctness of the loaded class or interface, checking for structural correctness (e.g., the format of the .class file), semantics (e.g., final classes are not subclassed), and bytecode instructions (e.g., type safety).
  • Preparation: Allocates memory for class variables and initializing them to default values. This step makes the class or interface ready for execution.
  • Resolution (optional): Involves replacing symbolic references in the loaded class or interface with direct references. It is an optional phase because it can be performed lazily, i.e., when the reference is used for the first time.

Initialization

This final phase involves executing the Java code that initializes static variables to their proper starting values. This is done through the execution of static initialization blocks and the assignment of values to static variables. Initialization is performed from the top of the class hierarchy downwards, meaning the superclass is initialized before the subclass.

The Class Loader Subsystem is vital for the JVM’s operation, as it not only loads classes and interfaces into the memory but also performs initial checks and preparations to ensure the integrity and security of the running Java application. This subsystem enables Java’s dynamic class loading capability, allowing classes to be loaded on an as-needed basis, which is particularly useful for reducing the startup time of Java applications and conserving memory resources.

2. Runtime Data Areas

The JVM organizes memory into several regions for various data storage purposes:

  • Method Area: Stores class structure (e.g., runtime constant pool, field and method data, and the code for methods and constructors), shared among all threads.
  • Heap: A runtime data area from which memory for all class instances and arrays is allocated. It’s shared among all threads.
  • Java Stack: Each Java thread has a private JVM stack, created at the same time as the thread. It stores frames, which contain local variables and partial results, and plays a part in method invocation and return.
  • PC Registers: Each thread has a PC (Program Counter) register to hold the address of the JVM instruction currently being executed.
  • Native Method Stacks: Not part of the Java language; these stacks support native methods (methods written in a language other than Java, e.g., C or C++).

3. Execution Engine

  • Interpreter: Reads bytecode stream then executes the instructions step by step.
  • Just-In-Time (JIT) Compiler: Improves the performance of Java applications by compiling bytecode into native machine code at runtime. The JVM then directly executes this native code, speeding up the process.
  • Garbage Collector: Automatically manages memory by reclaiming the memory used by objects that are no longer in use, thereby preventing memory leaks.

4. Native Interface and Libraries

  • Java Native Interface (JNI): Enables Java code running in the JVM to call and be called by native applications and libraries written in other languages, such as C or C++.
  • Native Libraries: Provide an interface to the native libraries necessary for the JVM to function. These include libraries for file handling, networking, and graphical interface functionality, among others.

Execution Flow

  1. Compilation: Java source code is compiled by the Java compiler into bytecode, which is stored in .class files.
  2. Loading: The Class Loader reads these files into the JVM.
  3. Execution: The Execution Engine interprets or compiles this bytecode into native machine code, which is then executed by the host machine.

The design of the JVM, with its components working harmoniously, allows Java applications to run efficiently and securely on any platform that has a compatible JVM, adhering to the principle of “write once, run anywhere”.

Avatar

Neelabh

About Author

As Neelabh Singh, I am a Senior Software Engineer with 6.6 years of experience, specializing in Java technologies, Microservices, AWS, Algorithms, and Data Structures. I am also a technology blogger and an active participant in several online coding communities.

You may also like

Blog Design Pattern

Understanding the Builder Design Pattern in Java | Creational Design Patterns | CodeTechSummit

Overview The Builder design pattern is a creational pattern used to construct a complex object step by step. It separates
Blog Tech Toolkit

Base64 Decode

Base64 encoding is a technique used to encode binary data into ASCII characters, making it easier to transmit data over