Once we write Java program in the IDE, the moment we press the run button the execution begins in the background. What happens behind the scenes are rarely thought or learnt. Here we will explore the underlying JVM (Java Virtual Machine) workings, it's internals and how it runs the Java code that we write.
Before we dig deep into the JVM Model, a review on how Java works.
- Source: Create a source document (.java file)
- Complier: Compile the document through a source code complier (.class file)
- Output: The Complier creates a new Byte-Code version which is capable of running in any other Java Enabled System. (WORA)
- Virtual Machine: Runs the Byte-Code version (.class file) by starting the JVM. Translation of bytecode to underlying machine code happens here.
Let's say we create a basic java program with a .java extension and we want it to be executed. We run the command "javac Filename.java" (compilation process). This creates a Filename.class file which is to be executed, so now we use the "java Filename" command to run your program. At the time we call this command we have created a JVM instance.
The JVM Architecture Model is shown below.
Overview:
JVM's primary purpose is to load and execute your application.
Your application's .class file as well as built-in class files of Java API (Collections, Object..) are loaded by the Class loader. The bytecode instructions from the Class Loader are then fed into the Execution engine which then executes the bytecode. The Execution engine here talks to the Host OS through native method calls in order to execute the instructions and then the bytecode is translated to Machine code(the underlying language which the platform understands).
3 Component breakdown of JVM:
1. Class Loader Subsystem
2. Runtime Data Area
3. Execution Engine
1. Class Loader Subsystem:
Responsible for loading the bytecode into memory from various sources(filesystem, .jar..). The activities the Class Loader performs are LOAD, LINK and INITIALIZE.
LOAD -
There are three types of class loaders Bootstrap, Extension and Application. The Java's internal class files(inside rt.jar distributed with JVM implementation), core API's are loaded by Bootstrap class loader. Extension class loader loads additional application jar(jre/lib/ext). The classes and values specified in your classpath environment variables are loaded by Application class loader.
LINK -
Verify verifies if the bytecode is valid (JVM Compatible). The Prepare phase is where memory for static variables are allocated. Example: Class variable, private static boolean isNum = false (default). Here a default initialization of value is allocated in the Prepare step.(Note: Memory Allocation happens only for class variables, not for instance variables). The Resolve stage is where the symbolic reference of the current class is changed to the actual reference.
INITIALIZE -
Here is where the class variables are initialized to the value in code, the static init block are run, the static variable values are set in memory locations.
Example: Class variable, private static boolean isNum = true (actual value set in code is assigned)
2. Runtime Data Area:
The Memory of JVM. There are five different areas namely the Method Area, Heap, Stack, PC Register and Native method stack. Java Stacks, PC Register and Native method stacks are per thread, while Method Area and Heap are per JVM.
Method Area -
Method area is where the metadata about the class are stored. Only class data such as static variables, class level constants are stored in memory of method area. 64mb is allocated for the method area which can be modified.
Heap -
Heap is where Object data, Instance variables are stores. When we instantiate an object such as using a new keyword, that object's creation and storage happens in the Heap.
PC Register -
This is a per thread runtime data area. It contains program counter which is basically a pointer to the next instruction which needs to be executed per thread.
Java Stacks -
Java Stacks contains stack frame(thread #1, #2, #3 will contain it's own stack frames) that contains data related to currently executing method. This is the memory area for Local Variables, Parameters, return values. Stacks are also executed per thread.
Native Method Stack -
For each thread that calls a native method a native method stack will be created.
3. Execution Engine:
Until now the data is just present in the memory area and yet to be executed. The .class file(bytecode) needs to be executed and here in the Execution Engine is where the actual instruction to be executed takes place. The Execution Engine has the Interpreter, JIT Complier and Garbage Collector.
Interpreter -
Interpreter takes the current instruction that has been set by the PC register and interprets the bytecode. It finds out native operation to be performed and executes it. The interpretation process is line by line and the disadvantage of that is, it interprets each time for the one method that has been called multiple times.
JIT Complier -
The efficiency and performance of the interpreter is important for the system to execute the code faster. The JIT Complier does just that. When Interpreter sees a repeated method call, JIT provides native code for that particular method on the fly so that re-interpretation does not happen.
Garbage Collector -
Cleans up unused classes, unreferenced objects and memory area.
Java Native Interface -
An Interface that interacts with the native method libraries. This loads the libraries during run-time which are required for execution.
Native Method Libraries -
Native Libraries in C, C++ that are required during the execution.
This is my first blog, I would like to contribute to the Tech Community on different topics. I started with JVM Architecture and hope this article provides you the concept of what happening behind the scenes.
Top comments (0)