The Mystery of JVM

Ranmal Dewage
8 min readMay 6, 2021
Source (https://www.guru99.com/java-virtual-machine-jvm.html)

When someone asked the question, what is the use of JVM most people will say it will be responsible for the conversion of bytecode to platform-specific machine code. That is a very generic definition of the JVM. There is more to JVM than that. First of all, we will understand what is meant by JRE, JDK.

  • JRE: Java Runtime Environment is the software layer that runs on top of a computer’s operating system. It will provide the class libraries, along with the JVM and other components that a given java program needs to run. In other words, JRE is the implementation of the JVM.
  • JDK: Java Development Kit is a software development kit that helps to develop applications in Java. It contains JRE and several development tools such as compilers, JavaDoc, Java Debugger, etc.

Java Virtual Machine (JVM)

Overall JVM Architecture

In simple terms, JVM is a specification that describes the requirement for JVM implementation. The JRE is responsible for the actual implementation. So when we run the java program, JRE deploys the platform-specific program to create a JVM instance in a given environment.

// complie and generate bytecode
> javac <file name>.java
// hand over the bytecode to JVM to do the necessary actions
> java <name of .class file generated>

When we use the “javac” command, it will generate a bytecode that is platform generic in nature. When we use the “java” command, the JRE will inform the operating system to create an instance of the JVM and hand over the bytecode file to the created JVM instance. As the next step, JVM will take the main method (as shown below) in the class structure for execution. Therefore one java program creates one JVM instance and a JVM instance exists only in the duration of the java program only.

public class Main{    // main method    
public static void main (String[] args){
}
}

When we talk about the JVM architecture, it has three major components as the Class Loader, Runtime Memory Area, and Execution Engine. In simple terms, Class Loader is responsible for the preparation and loading of java classes into the main memory. Runtime Memory Area is responsible for the holding of the runtime variables and data. Execution Engine is responsible for the execution of the Java program.

Class Loader

The responsibility of the class loader is to bring the .class files to the RAM(main memory) because JVM resides on the RAM. The class loader is also subdivided into three parts as Loading, Linking, and Initialization.

1. Loading

The main task of the loading phase is to load compiled .class files into the main memory. Therefore the order of loading the .class file is as follows. There are three class loaders as Bootstrap Class Loader, Extension Class Loader, and Application Class Loader. Starting from the lowest level, Application Class Loader delegates the received class loading request to Extension Class Loader. Then Extension Class Loader transfers the request to Bootstrap Class Loader. If we find the requested class in the Bootstrap path, the class will be loaded. Otherwise, it will transfer the request back to the Extension Class Loader to find the class from the Extension path. If that attempt also fails, the request will be transferred back to Application ClassLoader to find the class from the application classpath. Finally, if Application ClassLoader also fails to load the requested class, then we will get the ClassNotFoundException.

  • Bootstrap Class Loader: Root class loader. Super Class of Extension class loader and load the standard java packages like java.lang, java.net, java.util, java.io, etc. These packages are present inside the rt.jar file.
  • Extension Class Loader: Subclass of Bootstrap class loader and a superclass of Application class loader. It will load the files present inside the $JAVA_HOME/jre/lib/ext directory.
  • Application Class Loader: Subclass of Extension class loader and loads the files present in the classpath. The default classpath is set to the current directory of the application.

2. Linking

The Linking stage helps for the linking and the preparation of the loaded class or interface. Linking also divided into three subparts as Verification, Preparation, and Resolution.

  • Verification: This phase checks the structural correctness of the .class file by checking it against a set of constraints or rules. It will mainly check whether the code is written according to Java Language Specification and is it generated by a valid compiler. If verification fails due to some reason, we will get a VerifyException.
  • Preparation: In this phase, the JVM allocates the memory for the static and instance-level variables of a class or interface and initializes them with default values.
  • Resolution: In this phase, Symbolic References are replaced with the Direct References present in the runtime constant pool. What is meat by that is, if we have an object name student, it will replace that object name with the memory address location reserved for that created student object.

3. Initialization

This phase is responsible for the initialization of the classes or interfaces by calling the JVM <clinit> and <init> methods. These methods will include the calling class’s constructors, executing static blocks, and initializes static and instance-level variables with original values defined in the code.

<init> and <clinit> methods are generated by the compiler for the use of JVM at compile time. <init> method will include the constructors and other instance-level initializers for JVM. <clinit> method will include static variables and static block initializers for the JVM.

In a nutshell, Class Loader will read the Fully Qualified Class Name. Check whether it is an enum, interface, or a class. Read its immediate parent information, modifiers, static variables, and method details. After that, it will load it into the main memory.

One important thing, when we are creating an object from a particular class for the first time, the JVM will create an object from the “Class” type and then assigns our created object to that “Class” type object and place it in the heap. So that this “Class” object can be used to obtain the class level information.

Runtime Memory Area

When the JVM program runs on the Operating System Runtime Memory Area is the memory area assign for program execution. The Runtime Memory Area is again subdivided into five parts as Method Area, Heap Area, Stack, PC Register, and Native Method Stack.

1. Method Area

Method Area stores all the class level data such as class loader references, runtime constant pool, Field data, method data, and bytecode for methods and constructors. If the memory available in the method area is not sufficient for the program startup, JVM throws an OutOfMemoryError. Method Area is created on the JVM startup, and there is only one Method Area per JVM.

2. Heap Area

All the objects and their corresponding instance variables and arrays are stored on the heap. This is the runtime data area from which memory for all class instances and arrays are allocated. Heap Area is created on the JVM startup, and there is only one Heap Area per JVM.

3. Stack

For every thread, a separate Stack is created in the runtime memory area. Stack is responsible for storing the details of the method call in the program. For every method call, one entry will be made in the Stack which is called Stack Frame. Stack Frame track the details of Local Variables (method parameters and local variables in method), Operand Stacks, and Frame Data (symbols related to methods). If the thread needs more space in the stack than required, it will throw the StackOverflowError.

4. PC Register

Each thread has its own PC(Program Counter) Register to hold the memory address of the currently executing JVM instruction. If a native method is getting executed the value of the PC Register will be null or undefined. Once the instruction is executed, the PC Register value is updated with the next address of the JVM instruction.

5. Native Method Stack

The JVM contains a stack that supports native methods written in the languages like C and C++. For every thread created, a separate Native Method Stack is allocated for the execution of native methods.

Execution Engine

Execution Engine is responsible for the actual execution of the bytecode line-by-line. This component is also divided into three subparts as Interpreter, JIT Compiler and Garbage Collector.

1. Interpreter

The Interpreter read and executes bytecode instruction line-by-line. Due to the line-by-line execution interpreter will be slower, when we call repeated methods or code segments multiple times. The reason for that is every time a new interpretation is required, as shown below.

Interpretation for Repeated Code Segments

2. JIT Compiler

The Execution engine first uses the Interpreter to execute bytecode (convert the bytecode to native code/machine code). But when it finds repeated code segments, it uses the JIT Compiler to compile the entire bytecode to native code. Then for repeated methods, it directly provides native code by storing it in a cache. Therefore code can execute quickly, as shown below.

JIT Compiler execution of Bytecode

3. Garbage Collector

Garbage Collector operates on the Heap Area, and it is responsible for the collection and removal of the unreferenced objects in the heap. It is the process of reclaiming the unused runtime memory area by destroying them. Garbage collection has two phases as Mark and Sweep. Also, there are three types of garbage collections in JVM as Serial, Parallel, and G1. Garbage collections are done automatically by JVM at regular intervals and do not need to handle manually.

Java Native Interface (JNI)

JNI act as a bridge for allowing the supporting packages for other programming languages like C and C++, etc. It will be helpful in places where we need to code something that is not entirely supported by java, like some platform-specific feature that can be achieved by using C program language.

Native Method Library

A collection of C and C++ native libraries required for the execution engine, and they are accessible via Java Native Interface.

--

--

Ranmal Dewage

Software Engineer at Sysco Labs, Graduate of Sri Lanka Institute of Information Technology (SLIIT).