A Detailed Breakdown of the JVM
The Java Virtual Machine is something everyone uses, but not everyone knows how it works. Let's see how the JVM turns your work into bytecode.
Join the DZone community and get the full member experience.Join For Free
The JVM is the virtual machine on which Java code executes. It's responsible for converting byte code into machine-specific code.
HotSpot JVM Architecture
Diagram: HotSpot JVM Architecture
Now, let’s discuss each and every component of JVM architecture in detail. It consists of a variety of components, and we'll start with the classloader subsystem.
Classloader Subsystem of the JVM
Classloader is a subsystem of the JVM. Classloader is used to load class files. It verifies class files using a bytecode verifier. A class file will only be loaded if it is valid.
Runtime Data Areas of JVM
The method area is also called the class area. The method area stores data for each and every class, like fields, constant pools, and method data and information.
The heap is the place where all objects are stored in JVM. The heap even contains arrays because arrays are objects.
Java Threads (Java Thread Stacks)
You must know that each and every thread has its own stack. How are stack frames created when threads call new methods? As we know, each and every thread has its own stack. Whenever a new method is called, a new stack frame is created, and it is pushed on top of that thread's stack.
What do thread stacks contain? They have all the local variables, all the parameters, and all the return addresses. Stacks never store objects, but they store object references.
Program Counter Registers (PC Registers)
The program counter registers contain the address of the instructions currently being executed and the address of next instruction as well.
Native Internal Threads (Native Thread Stack)
Native internal threads contain all the information related to native platforms. For example, if we're running the JVM on Windows, it will contain Windows-related information. Likewise, if we're running on Linux, it will have all the Linux-related information we need.
The Execution Engine contains the JIT (Just In Time) Compiler and Garbage Collector compiler, as well as the Interpreter
The JIT Compiler compiles bytecode to machine code at runtime and improves the performance of Java applications.
Of course, JIT compilation does require processor time and memory usage. When the JVM first starts up, lots of methods are called. Compiling all of these methods might affect startup time significantly, though a program ultimately might achieve good performance.
Methods are not compiled when they are called the first time. For each and every method, the JVM maintains a call count, which is incremented every time the method is called. The methods are interpreted by the JVM until the call count exceeds the JIT compilation threshold (the JIT compilation threshold improves performance and helps the JVM to start quickly. The threshold has been selected carefully by Java developers for optimal performance. The balance between startup times and long-term performance is maintained).
Therefore, very frequently used methods are compiled as soon as the JVM has started, and less frequently used methods are compiled later.
After a method is compiled, its call count is reset to zero, and subsequent calls to the method increment its call count. When the call count of a method reaches a JIT recompilation threshold, the JIT compiler compiles method a second time, applying more optimizations as compared to optimizations applied in the previous compilation. This process is repeated until the maximum optimization level is reached. The most frequently used methods are always optimized to maximize the performance benefits of using the JIT compiler.
Let’s say the JIT recompilation threshold = 2.
After a method is compiled, its call count is reset to zero and subsequent calls to the method increment its call count. When the call count of a method reaches 2 (i.e. JIT recompilation threshold), the JIT compiler compiles the method a second time, applying more optimizations.
Garbage collection is the process by which the JVM clears objects (unused objects) from the heap to reclaim heap space.
Interpreter is responsible for reading the bytecode and then executing the instructions.
Native Method Libraries of the JVM
The native method interface is an interface that connects the JVM with the native method libraries for executing native methods.
If we are running the JVM (a Java application) on Windows, then the native method interface (Windows method interface) will connect the JVM with the Window method libraries (native method libraries) for executing Windows methods (native methods).
You may write your application purely in Java, but there are certain situations where Java code alone might not meet your requirements. Programmers use the JNI to write the Java native methods when an application cannot be written purely in Java.
Read more about the JNI here.
The most important JVM Components related to performance are:
- JIT (Just In Time) Compiler and
- Garbage collector
Diagram: key components of HotSpot JVM for performance.
Three components (the heap, JIT (Just In Time) compiler, and Garbage collector) are related to JVM’s performance tuning.
All objects are stored in the heap, and the garbage collector manages the heap at JVM initialization.
There are many VM (JVM) options for:
- Increasing and decreasing the heap size for managing object for best performance.
- Selecting different garbage collectors, depending on your requirement.
Meanwhile, as for the JIT Compiler JIT:
- The JIT Compiler compiles bytecode to machine code at runtime and improves the performance of Java applications.
- JIT Compiler tuning is rarely needed for newer versions of the JVM.
How Is the Java Platform an Independent Language?
Once source code (i.e. a .java file) is compiled on one platform (bytecode is formed), that bytecode can be executed (interpreted) on any other platform running a JVM.
Every platform has a different JVM implementation. From here, you can download the JVM for different platforms. For example, the JVM for Windows is different from the JVM for Linux.
This diagram helps demonstrate its independence:
The JVM is a very powerful and flexible runtime platform for languages such as Java, Groovy, Scala, and Clojure. The JVM provides a large number of libraries and is completely interoperable with Java.
Published at DZone with permission of Ankit Mittal. See the original article here.
Opinions expressed by DZone contributors are their own.