JDK, JRE, JVM

Execution of a Java Program

Before understanding the JVM, let’s first understand how a Java source file is executed.

  1. First we write the java code and store it with the .java extension.
  2. When we compile it, Java compiler (javac.exe) converts this source code to a .class file with the same name. This file contains platform independent bytecode which is understood by JVM.
  3. Then we run this class file in JVM, who understands this bytecode and translates it into underlying OS readable machine code which machines can execute.
What is JVM

JVM (Java Virtual Machine) provides a runtime environment in which java bytecode can be executed. JVM loads, verifies and executes Java bytecode.

JVM can also run programs written in other programming languages that have been translated to Java bytecode. Because JVM cares about valid bytecodes, not from where and how that bytecode is generated.

In many languages (like C, C++), the compiler produces a platform dependent code, which can be executed directly by the underlying operating system (OS) and physical CPU. So for different operating systems, you have to compile the code separately. But when the designers of the Java language decided to make the language platform independent, they decided to first generate an intermediate platform independent bytecode (not machine code). The JVM, that is different for different OS, will accept the same bytecode and its just-in-time (JIT) compiler will convert that bytecode into native machine code during runtime.

You must be wondering, why do we call it a virtual machine? Normally a machine is supposed to do some task and it has physical existence, you can see it and touch it. But virtual means something that doesn’t exist physically. As JVM just provides the runtime environment for your java application and doesn’t have any physical existence, it’s called a virtual machine.

Functionalities of JVM

These are the basic tasks that are performed by JVM –

  • Loads the code.
  • Verifies the code.
  • Executes the code.
  • Provides runtime environment for your application.
  • Provides a memory area.
JVM Architecture

There are three main subsystems in JVM Architecture:

  1. ClassLoader
  2. Memory Area
  3. Execution Engine
Class Loader

It is used to load class files. Whenever we run the java program, the class loader loads it to JVM. There are three built-in class loaders –

  1. Bootstrap class loader: The bootstrap class loader loads rt.jar (contains java built-in classes) and other core Java libraries located in the /jre/lib directory.
  2. Extension class loader: The extension class loader is a child of the bootstrap class loader, and takes care of loading java classes from /lib/ext directory.
  3. System class loader: The system class loader is a child of extension class loader and it loads your application specific classes.

Now the question is, which class loader will load the class? To understand it, let’s assume, we are trying to load TestJava.class.

  1. First System Class Loader will ask the Extension Class Loader to find the class and Extension Class Loader will ask Bootstrap Class Loader to find the class.
  2. If Bootstrap Class Loader finds the class, then that class will be loaded to JVM. Otherwise, the responsibility for loading the class is returned to the Extension Class Loader.
  3. Extension Class Loader then tries to find the class. If found, the class will be loaded to JVM. Otherwise, the responsibility for loading the class is returned to the System Class Loader.
  4. Then the System class loader tries to find the class and if found, loads the class in JVM. If System Class Loader cannot load the class, ClassNotFoundException will be thrown.

Now let’s understand how the class will be loaded. Let’s assume TestJava.class is being loaded. There are three main steps –

  1. Loading: In this process, the class loader will load the TestJava.class file and will create an Object of Java.lang.Class. Then it will store this Class object in the method area. This object can be used to get class level information like name of the class, name of the parent class, method and variable information etc. To get this object, we can use the getClass() method.
  2. Linking: In this process, the Bytecode verifier ensures that the class file is valid. Then memory is allocated to all static variables and they are initialized with default values. Then the class loader calls loading and linking of other classes that our class references.
  3. Initialization: In this final phase, static variables are assigned with their values and static blocks are executed.
Memory Area

Below are the important memory areas in JVM –

  • Method Area: It stores class structures, code for method and constructors etc.
  • Heap Memory: Instances of a class and arrays are stored in heap memory.
  • Stacks: Stacks store the partial results and local variables of a program. Whenever a thread is created, a corresponding stack is created. When we invoke a method, a new frame is created on top of the stack which contains values specific to that method. When the method finishes execution, the corresponding frame is flushed.
  • PC Registers: A program counter (PC) register is created every time a new thread is created. Program counter keeps track of the current statement that is being executed.
  • Native Method Stacks: Native methods are those which are written in languages other than Java. It is same as stack but it is used for native methods.
Execution Engine

Execution engine executes the byte codes loaded by the class loader. It has two important part –

  • JIT Compiler: It compiles the bytecodes to machine code at run time. When a method is already compiled, the JVM calls the compiled code of that method directly.
  • Garbage Collector: It tracks each and every object available in JVM heap space and removes the unused ones. We shall discuss on this in detail in separate topic.
How to install only JVM

You cannot install JVM independently. When you install Java in your system, you basically install either JRE or JDK. JVM comes along with both of them.

JRE: JVM just executes the bytecode. But to run a Java application, you need some Java class libraries like rt.jar and other supporting files. Java Runtime Environment (JRE) provides that. But it doesn’t provide any tool to compile a Java source code or debug the source code. So, if you want to just run a Java application, you need JRE, which contains JVM along with other required things that are necessary to run a Java program.

JDK : What if you want to write a Java application and compile it using Java compiler javac.exe? Maybe you want to debug it to identify some issues. Java Development Kit (JDK) can help you with that. JDK contains JRE to run a Java application and other require components to compile and debug Java code.