Understand the JVM-virtual machine stack from the shallower to the deeper

The virtual machine stack is one of the thread-private modules in the JVM memory structure. The feature is first-in-last-out. This feature determines that the calling process of the method is performed in the stack. Every time a method is called, a stack frame corresponding to this method is generated in the stack. The stack frame includes four parts of the local variable table, operand stack, dynamic connection and method return address. When the memory of a stack is not enough to hold enough stack frames, that is, the so-called stack attempt is greater than the virtual allowable depth, a StackOverFlowError exception will be thrown. When the stack is expanded and the JVM memory is insufficient, OutOfMemoryError will be thrown. Next, the stack is introduced through graphics and specific code

Stack structure

The structure of the stack is shown in the following figure:
Insert picture description here
Method1 and Method2 correspond to two stack frames, and it can be seen from the figure that Method1 is called in Method2. There are 4 core parts in each stack frame, which are local variable table, operand stack, dynamic link, and method return address. Of course, there are other parts as well, which are not introduced here.

Local variable table

As the name knows, it is used to store local variables, that is, all variables in a method are stored in this table, and each variable is assigned at runtime.

Operand stack

It is used to store the values ​​of all basic type variables in the method, that is, the specific values ​​corresponding to the variables in the local variable table.

Dynamic link

It is used to convert the symbolic reference pointing to a calling method in the constant pool into a direct reference of the calling method.

Method return address

It can be understood as the line number used to store the location where this method is called. For example: Method2 calls Method1 on line 7, then the corresponding method return address in Method1's stack frame is 7. Of course, it can be understood this way, but in fact it is a section of address.

Code analysis

Through the above introduction, although there is a general understanding, there may be some ambiguities. Next, we will analyze it through a piece of code.

public class JVMTest {
    
    
    public int add(){
    
    
        int a = 3;
        int b = 10;
        int c = a+b;
        return c;
    }
    public static void main(String[] args) {
    
    
        JVMTest jvmTest = new JVMTest();
        int result = jvmTest.add();
        System.out.println("The result is : "+result);
    }
}

This code is very simple. There are two methods in a class, main and add methods. The add method is called in the main method, and the add method performs a simple addition operation. So how does this code in the virtual machine stack work at runtime? Here we analyze the bytecode file of this code. Bytecode files can be translated into readable instruction codes by javap command. The code by javap -c JVMTest.classfollowing results were obtained:

Compiled from "JVMTest.java"
public class com.research.JVMTest {
    
    
  public com.research.JVMTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public int add();
    Code:
       0: iconst_3
       1: istore_1
       2: bipush        10
       4: istore_2
       5: iload_1
       6: iload_2
       7: iadd
       8: istore_3
       9: iload_3
      10: ireturn

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/research/JVMTest
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #4                  // Method add:()I
      12: istore_2
      13: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      16: iload_2
      17: invokedynamic #6,  0              // InvokeDynamic #0:makeConcatWithConstants:(I)Ljava/lang/String;
      22: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      25: return
}

It looks like a headache, take it easy and don't worry, here we mainly use the add method and the main method to analyze how the four core areas in the stack work. Before the analysis, we have to read the instructions in it, there is no need to remember, when reading, you can query through the official jvm instruction manual . Here I organize the 10 instructions in the add method as follows:

instruction effect Scope
iconst_3 Int type constant value 3 enters the operand stack Operand stack
istore_1 Store the top int value of the stack into the first local variable From the operand stack to the local variable table
bipush Push the single-byte constant value (-128~127) to the top of the stack Operand stack
istore_2 Store the int value on the top of the stack into the second local variable From the operand stack to the local variable table
iload_1 Push the first int type local variable to the top of the operand stack From local variable table to operand stack
iload_2 Push the second int type local variable to the top of the operand stack From local variable table to operand stack
iadd Add the two int values ​​on the top of the stack and push the result onto the top of the operand stack Operand stack
istore_3 Store the int value on the top of the stack into the third local variable From the operand stack to the local variable table
iload_3 Push the third int type local variable to the top of the operand stack From local variable table to operand stack
ireturn Return int from current method Operand stack

PS: Students who have studied assembly in university should feel that these instructions are familiar to me. Of course, I also think so.
The execution process of the entire add method is shown in the following figure. Each block represents an instruction. The instruction shows the stack frame The dynamic changes of various parts, among which the local variable table and the operand stack correspond to a piece of memory. The operand stack is a temporary storage area, which temporarily stores variables and calculation results, and the method return address is used as the exit of this method.
Insert picture description here
It is not difficult to find that the definitions in the add method are all basic type variables. If the definition is a reference type variable, what will happen in the local variable table. Look to the main method: The
main method defines a local variable of reference type, and its value is an address pointing to an object. Of course, this object is not stored in the operand stack, but in the heap ( from shallow to deep understanding of JVM -Heap ). At this point, a bridge can be built between the virtual machine stack and the heap: the
Insert picture description here
light will soon be seen, and the last core part is left: dynamic linking. Since it is a dynamic link, how is it a dynamic method.
When the Java source file is compiled into a bytecode file, all variables and method references are saved as symbolic references in the constant pool (method area) of the class file. For example, when describing that a method calls another method, it is represented by the symbol reference to the method in the constant pool (in the bytecode main method above: 17: invokedynamic #6, 0), then the function of dynamic linking is to convert these symbol references into The direct reference of the calling method.

So far, the working principle of the virtual stack has been introduced. As for the working principle of other parts, please refer to the following blog post to
understand JVM-memory structure
from shallow to deep understand JVM-heap
from shallow to deep understand JVM-program counter

Guess you like

Origin blog.csdn.net/hongyinanhai00/article/details/113856144