How JVM Works ☕️
Unveiling the Internal Workings of the Java Virtual Machine (JVM)
Introduction
Java has long been one of the most widely used programming languages, owing much of its success to the Java Virtual Machine (JVM). The JVM is an integral part of the Java ecosystem, providing a stable, secure, and high-performance runtime environment for Java applications. In this article, we will dive deep into the internal workings of the JVM, and also look at the diagrams to provide a visual understanding of its components and operations.
Overview of the Java Virtual Machine (JVM)
The JVM is a virtual machine that runs Java bytecodes, which are compiled from Java source code. The JVM provides a platform-independent execution environment that allows Java applications to run on any device or operating system.
The JVM has three main components:
- Class Loader.
- Runtime Data Area.
- Execution Engine.
Class Loader
The Class Loader is responsible for loading, linking, and initializing classes and interfaces. It follows a three-step process:
a. Loading: The Class Loader reads the .class files and generates the corresponding binary data.
b. Linking: The linking phase verifies the correctness of the bytecode, prepares the static fields, and resolves the symbolic references.
c. Initialization: The initialization phase assigns the initial values to the static fields and executes the static initializer blocks.
Runtime Data Area
The Runtime Data Area consists of five components:
a. Method Area: It stores the class data, such as runtime constant pool, field data, method data, and method code.
b. Heap: It is the memory area where objects and their corresponding instance variables are allocated.
c. Stack: It is a collection of stack frames, where each frame represents a method’s local variables and operand stack.
d. Program Counter (PC) Register: It holds the address of the next instruction to be executed.
e. Native Method Stack: It holds native method information.
Execution Engine
The Execution Engine interprets and executes the Java bytecodes stored in the method area. It consists of three main components:
a. Interpreter: It reads and interprets the bytecodes one by one, which can result in slower execution.
b. Just-In-Time (JIT) Compiler: It compiles the bytecodes into native machine code, improving the execution speed.
c. Garbage Collector (GC): It reclaims memory by identifying and removing objects that are no longer needed.
Let’s understand the working of the JVM with a simple Java program:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
- The Java source code is compiled into bytecode by the Java Compiler (javac) and saved as a .class file.
- The Class Loader loads the HelloWorld class, links it, and initializes it.
- The Execution Engine interprets the bytecode, executing the main() method.
- The main() method is pushed onto the stack as a new stack frame.
- The System.out.println() call is encountered, and the “Hello, World!” string is stored in the Runtime Data Area’s heap.
- The System.out.println() method is invoked, and the reference to the “Hello, World!” string is passed as an argument.
- The Execution Engine executes the native code for System.out.println(), which results in the “Hello, World!” string being printed to the console.
- After the main() method completes, its stack frame is popped from the stack, and the JVM terminates.
Diagram for HelloWorld Execution:
Conclusion
In this article, we have explored the internal workings of the Java Virtual Machine (JVM) with diagrams. By understanding the JVM’s components and processes, such as the Class Loader, Runtime Data Area, and Execution Engine, developers can better grasp the underlying mechanisms that make Java a versatile, platform-independent, and high-performance language.