A Preliminary Study of JVM Architecture

Every Java developer knows that bytecode is executed via JRE (Java Runtime Environment). But they may not know that the JRE is actually implemented by the Java Virtual Machine (JVM), which analyzes the bytecode, interprets and executes it. As a developer, it is very important to understand the architecture of the JVM because it enables us to write more efficient code. In this article, we will take a deep dive into the JVM architecture in Java and the various components of the JVM.
 
JVM
 
A virtual machine is a software implementation of a physical machine. The design philosophy of Java is WORA (Write Once Run Anywhere). The compiler compiles the Java files into Java .class files, and then the .class files are fed into the JVM, which performs the loading and execution of the class files.
 
How does the JVM work?
 
The JVM is divided into three main subsystems: 
Class Loader Subsystem 
Runtime Data Area 
Execution Engine
1. Class loader subsystem
Java's dynamic class loading is handled by the class loader subsystem, which includes loading and linking, and instantiation of the class file begins when the class file is run, the first time the class is referenced, rather than at compile time.
1.1 Loading
Boot Strap class loader, Extension class loader and Application (Class loader is the three class loader that implements the class loading process.
(1) Boot Strap class loader: responsible for loading classes from the boot class path, except rt.jar, which has the highest priority;
(2) Extension class loader: responsible for loading the classes in the ext folder (jre\lib);
(3) Application class loader: Responsible for loading application-level class paths, paths specified in environment variables, and other information.
The above class loader follows the Delegation Hierarchy Algorithm when loading class files.
1.2 Links
(1) Verify: The bytecode verifier will verify whether the generated bytecode is correct, and if the verification fails, a verification error will be prompted;
(2) Prepare (Prepare): For all static variables, memory will be allocated with default values;
(3) Resolve: Signed memory references are replaced with original references from the Method Area.
1.3 Initialization
This is the final stage of class loading, all static variables will be given their original values ​​and static blocks will be executed.
2. Runtime data area
The runtime data area can be divided into 5 main components:
(1) Method Area: All class-level data will be stored here, including static variables. Each JVM has only one method area, which is a shared resource;
(2) Heap Area: All objects and their corresponding instance variables and arrays will be stored here. There is also only one heap area per JVM. Since methods and heap regions share memory for multiple threads, the stored data is not thread-safe;
(3) Stack Area: For each thread, a separate runtime stack will be created. For each method call, an entry is made in stack memory, called a stack frame. All local variables will be created in stack memory. The stack area is thread-safe because it does not share resources. The stack frame is divided into three sub-elements:
Local Variable Array (Local Variable Array): related to methods, involving local variables, and storing corresponding values ​​here 
Operand stack: If any intermediate operations need to be performed, the operand stack acts as a runtime workspace to perform operations 
Frame Data: All symbols corresponding to methods are stored here. In case of any abnormality, the captured block information will be kept in the frame data; 
(4) PC Registers (PC Registers): Each thread has a separate PC register for saving the address of the currently executing instruction. Once the instruction is executed, the PC register will be updated by the next instruction;
(5) Native method stacks (Native Method stacks): Native method stacks save native method information. For each thread, a separate native method stack is created.
3 Execution engine 
The bytecodes allocated to the runtime data area will be executed by the execution engine, which reads the bytecodes and executes them one by one.
(1) Interpreter: Interpreter interprets bytecode faster, but executes slowly. The disadvantage of interpreters is that when a method is called multiple times, a new interpreter is required each time;
(2) JIT编译器:JIT编译器消除了解释器的缺点。执行引擎将在转换字节码时使用解释器的帮助,但是当它发现重复的代码时,将使用JIT编译器,它编译整个字节码并将其更改为本地代码。这个本地代码将直接用于重复的方法调用,这提高了系统的性能。JIT的构成组件为:
中间代码生成器(Intermediate Code Generator):生成中间代码 
代码优化器(Code Optimizer):负责优化上面生成的中间代码 
目标代码生成器(Target Code Generator):负责生成机器代码或本地代码 
分析器(Profiler):一个特殊组件,负责查找热点,即该方法是否被多次调用; 
(3) 垃圾收集器(Garbage Collector):收集和删除未引用的对象。可以通过调用“System.gc()”触发垃圾收集,但不能保证执行。JVM的垃圾回收对象是已创建的对象。
Java本机接口(JNI):JNI将与本机方法库进行交互,并提供执行引擎所需的本机库。
本地方法库(Native Method Libraries):它是执行引擎所需的本机库的集合。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326180996&siteId=291194637