The difference between a virtual machine and a physical machine . Both have code execution capabilities . The execution engine of the physical machine is built on the processor 、 Hardware 、 On instruction set and operating system . The execution engine of virtual machine has its own implementation . Therefore, we can establish the structure relationship between instruction set and execution engine .
Personal understanding ： It's divided into three parts . Each memory area of runtime is introduced 【 Program counter 、java Virtual machine stack 【 Local variable table 、 The stack of operands 、 Dynamic links 、 Method return address 】、 Native Method Stack 、 Method area 、 Pile up 】. Method call 【 analysis 、 Assignment 】、 Method execution 【 Parsing performed 、 Compile implementation 】
1. Runtime stack frame structure
Stack frame is a data structure for virtual machine to call and execute methods . Is the stack element of the virtual machine stack . This part of the content and running memory area JAVA There are overlapping parts in the stack frame structure of virtual machine stack . A stack frame contains a local variable table 、 The stack of operands 、 Dynamic link and method return address four parts . The size of the local variable table and the depth of the operands required by the stack frame are determined in the compilation phase .
Java The explanation execution engine of the virtual machine is called “ Stack based execution engine ”, The stack refers to the stack of operands .
Program counter 、 Virtual machine stack 、 The local method stack is thread level . Born with what's ready 、 With the death of the thread . signify , Each thread has its own java Virtual machine stack . The method invoked in this thread is used as a stack frame , Stack pressing and spring stack .
(1) Local variable table
It is mainly used to store the parameters of the method and the local variables defined within the method . A local variable table is a set of value storage spaces .
A method is released only after the call is completed . thus , If a method has previously defined a large memory object , however , This memory is no longer needed in the second half of the method . meanwhile , The second half takes a long time to execute . In this case , Will cause the first half of the creation of a large number of objects are not needed , And because the method is not executed, it is released . This part of memory will be occupied uniformly and cannot be released . therefore , It can also be within the method , Assign variables that you don't need to continue to use as null. The garbage collector can recycle in time .
(2) The stack of operands
The depth of the stack of operands is determined at compile time . In the process of method execution , Getting data from a local variable table , Pressing stack . When calculation is needed, data is obtained from the top of the stack according to the operation symbol , Put the results on the stack .
(3) Dynamic links
stay class In file , One method needs to call other methods . The symbolic references of these methods need to be converted into direct references in memory addresses . These symbol references exist in the constant pool in the method area .
The stack frame of each stack contains a symbolic reference to the method in the runtime constant pool . The purpose of holding this reference is to support dynamic linking in method calls .
Some of these symbolic references will be converted to direct references during class loading or the first time they are used . This transformation is called static parsing . The other part will be converted into a direct reference during each run . This part is called dynamic link .
(4) Method return address
When a method starts executing , There may be two ways to exit the method . Complete the exit normally and Abnormal completion exit .
No matter what method is used to exit , You need to return to the Where the method is called , The program can continue , Method may need to save some information in the current stack frame , It is used to help him recover the execution state of its upper level methods .
2. Method call
Method call It's not the same as method execution , The core task is ： Determine the version of the called method 【 Which method is called 】. It doesn't involve the specific execution process inside the method .
All calls between methods are in class The file stores symbol references . Not the specific method 【 The entry address of the actual runtime memory layout 】 A direct reference to . The direct reference to the target method needs to be determined during class loading and even during runtime .
Called The target method It is a symbol reference in the constant pool of the method area . If the program can determine the version of the method call before it actually runs , And the version of the method call does not change during runtime . In this situation , Loading in the class Analytic stage , Will convert symbolic references to direct references . The call to this method is called parsing .
accord with “ The compile time is known , The operation period is immutable ” The required method of , It mainly includes static method and private method . The former is directly related to a class , The latter is not externally accessible . They are characterized by the impossibility of rewriting other versions by inheritance or other means . therefore , Methods are called by parsing .
Analytic correspondence 5 Method call instructions ：
① Invoke static : Call static methods
② Invoke special： Call instance constructor <init> Method , Private methods and parent methods .
③ Invoke virtual： Call all virtual methods .【final The method of decoration , Because it can't be covered , There is no other version , therefore , There is no need for polymorphic selection of methods 】
④ Invoke Interface： Call interface method ( polymorphic )
⑤ Invoke dynamic：
Parsing is a static process , In the parsing phase of the link phase of class loading , It will convert all the symbol references involved into direct references .
① Static Dispatch
All dependence Static type 【 The declaration type of the parameter 】 A dispatch to locate a method version is called a static dispatch .
The most typical static dispatch is heavy load . In the compilation phase , Depending on the static type of the parameter, you can determine which version of the method is executed . Static dispatch occurs during the compile phase , therefore , Statically allocated actions are not performed by virtual machines .
② Dynamic dispatch
The dispatch of the version of a method that is determined by the actual type at run time is called dynamic dispatch .
The most typical example of dynamic dispatch is rewrite .
JVM Of invoke virtual Instructions , The parsing process of this instruction helps us understand the nature of rewriting more deeply . The specific parsing process of this instruction is as follows
- Find the object that the first element at the top of the operand stack points to Actual type , Write it down as C.【 Get the type of the actual object 】.
- If in type C Find a method that matches both the descriptor and the simple name in the constant , Check the access rights , If it passes, it returns the direct reference to this method , Find the end ; If it doesn't go through , Illegal access exception is returned 【 stay C Find the method that matches the current method 】
- If in type C Not found in , Then according to the inheritance relationship, from bottom to top C Each parent class of 2 Step by step search and verification process 【 First look for... In the subclass , No more searching in the parent class . Contrary to parental delegation , Parent delegation is first handled in the parent loader , Overloading first matches in subclasses 】
- If you never find the right way , Then throw an exception with an abstract method error .
③ Single dispatch and multiple dispatch
(3) Dynamic type language support
3. Execution engine
The core understands how virtual machines execute bytecode instruction sets in methods . In fact, it is based on the stack bytecode to explain what the execution engine does .JAVA When the virtual machine executes the code , There are Explain to perform 【 Execute... Through interpreter 】 and Compile implementation 【 Generating local code execution through a compiler 】 Two options .
Execution engine ： Parse bytecode instructions / Compile to local machine instructions on the corresponding platform . In short , The execution engine acts as a translator between high-level language and machine code .
among , In the execution engine Interpreter （interpreter） Provides interpretation and execution functions .JIT Completer It provides the function of compiling and executing .
(1) Explain to perform
JVM After getting the bytecode . Through the parser Interpreter Parse it into the final machine code .
(2) Stack based instruction set and register based instruction set
Stack based instruction set ： Save the instruction set on the stack . Most of them are zero address instruction sets , Depending on the stack of operands .Java The instruction set output by the compiler is a stack based instruction set .
PC Computer support is based on The instruction set of registers .
(3) Stack based interpreter execution process
To execute sequentially from an instruction set , Passing local variables through the stack of operands , Finally, it is generated in the local variable table . The process of operation is to get data from the local variable table , And then put it in the stack of operands . Pop up from the stack of operands for calculation . After that, the results will be stacked .
Put the operands on the stack first , Then pop up the data in the stack of operands into the local variable table , In this way, the variables of the local variable table are assigned step by step . During the operation , Get data from the local variable table and push it into the stack of operands . And then according to the instructions , Pop up data from the stack of operands for calculation , After that, the calculation results will be pushed onto the stack .
(4) Compile implementation
JVM The platform provides a timely compilation technology , The purpose of just in time compilation is to avoid functions being interpreted and executed . Instead, the entire function body is compiled into machine code . Every time a function is executed , Directly execute the compiled machine code . This way, we can greatly improve efficiency .