JVM-Virtual Machine Stack

Virtual machine stack

Due to the cross-platform design, java instructions are designed according to the stack. Because different platforms have different CPU architectures, they cannot be designed based on registers.
advantage:Cross-platform, the instruction set is small, the compiler is easy to implement, the disadvantage is that the performance is reduced, and the same function requires more instructions.
Set the stack size: -Xss 1m (size)
Insert picture description here

Runtime stack frame structure

The stack frame is used to store the local variable table, operand stack, dynamic link, and method exit. Each method is called to the process of execution, which corresponds to the process of a stack frame into the stack to the stack. All bytecode instructions run by the execution engine are only for the current stack frame (top of the stack).

Local variable table

  1. The local variable table is a storage space for a set of variable values, used to store method parameters and local variables defined in the method.

  2. The size of the local variable table is known at compile time. The size of the local variable table in the stack can be calculated according to the local variable table parsed by the class file.

  3. The capacity of the local variable table is based on the variable slot (slot) as the smallest unit. If it is a long or double type, two slots are required and the index records the first one. What

  4. Assignment rules: if it is an instance method, then 0 index is stored in this and the
    rest of the parameters are assigned in order
    according to the variable order and scope assignment defined in the method body

  5. Local variable slots can be reused

  6. The variables in the local variable table are also important garbage collection root nodes. As long as the objects referenced by the local variable table will not be recycled.

Operand stack

  1. The operand stack is a last-in, first-out stack, and the maximum depth is determined at compile time. Mainly used to save the intermediate results of the calculation process, and at the same time as a temporary storage space for variables in the calculation process.

  2. The stack is empty when a method is executed. In the process of execution, the stack will be continuously operated through bytecode instructions.

  3. In most virtual machine implementations, adjacent stack frames will partially overlap. Let part of the operand stack of the lower stack frame overlap with the upper part of the local variables

  4. If the called method has a return value, then put the return value on the stack

Dynamic link

Each frame contains a reference to the method to which the frame belongs in the runtime constant pool. This reference is held to support dynamic linking during method invocation.This quote is the population of some information about this method(We call that some symbol references in the runtime constant pool are converted into direct references at runtime called dynamic linking)
Insert picture description here

Method return address

When a method starts to execute, there are only two ways to exit the method. The first: the execution engine encounters a bytecode instruction returned by any method. The second type: encounter an exception. Regardless of the exit method, you need to return to the original method. Under normal circumstances, when exiting normally, the PC counter value of the main tuning method is just fine. It will not be saved when exiting abnormally.
The process of method exit:

  1. Pop the current stack frame
  2. Restore the local variable table and operand stack of the upper method
  3. If there is a return value, push it to the operand stack
  4. Adjust the value of the PC counter to the instruction following the call instruction

Some other information

Method call

Parsing

In the parsing stage of class loading, some of the symbol references are converted into direct references. The premise that this kind of analysis can be established is that the method has a definite calling version before the program actually runs, and the runtime cannot be changed. In other words, the calling target is determined when the program code is written and the compiler compiles it. The invocation of such methods is called resolution, such as static methods, final methods, private methods, instance constructors, and instance constructors. Because these will not be changed, they are not virtual methods.

  1. invokestatic calls a static method
  2. invokespecial calls the instance constructor init method, private method, method in the parent class
  3. invokevirtual calls virtual methods (final methods are also inside but will be resolved)
  4. invokeinterface calls an interface method, and an implementation method will be determined at runtime
  5. Invokedynamic now dynamically resolves the method of invoking the qualifier at runtime, and then executes the method. (Added by java7, to achieve dynamic type language)

The resolution call is a static process, which is completely determined during compilation. And another main method call form: dispatch.

Dispatch

  • Static dispatch
    look at the output of the following program
public class TestClass {
    
    
    static abstract class Human{
    
    }
    static class Man extends Human{
    
    }
    static class Woman extends Human{
    
    }
    public void sayHello(Human guy){
    
    
        System.out.println("Hello guy");
    }
    public void sayHello(Man guy){
    
    
        System.out.println("Hello man");
    }
    public void sayHello(Woman guy){
    
    
        System.out.println("Hello woman");
    }

    public static void main(String[] args) {
    
    
        Human man = new Man();
        Human woman = new Woman();
        TestClass tc = new TestClass();
        tc.sayHello(man);
        tc.sayHello(woman);
    }

}
//输出:
//Hello guy
//Hello guy

The above is the method overload (OverLoad) that is to sayStatic dispatch of methods

Human man = new Man();

Human called above code variable static type , or the type of call appearance, while the back of the actual type of variables referred to Man, or called runtime type.
The static type of the variable itself will not be changed, and the final static type is known at compile time; and the actual type change is determined at runtime.
At the time of the call, we have confirmed that we are using the tc object, which overloaded version to use depends on the number of passed parameters and the data type. Because the compiler passes the static type of the parameter instead of the runtime type when overloading, the output is all Hello guy.

  • Dynamic dispatch
    Look at the output of the following program
public class TestClass {
    
    
    static abstract class Human{
    
    
        protected abstract void sayHello();
    }
    static class Man extends Human{
    
    
        @Override
        protected void sayHello() {
    
    
            System.out.println("Hello man");
        }
    }
    static class Woman extends Human{
    
    
        @Override
        protected void sayHello() {
    
    
            System.out.println("Hello woman");
        }
    }


    public static void main(String[] args) {
    
    
        Human man = new Man();
        Human woman = new Woman();
        man.sayHello();
        woman.sayHello();
    }
}
//输出:
//Hello man
//Hello woman

Obviously, anyone who knows java knows that this is a rewrite of java. So how does the java virtual machine determine which method to call?
The process of calling the method of the java virtual machine is the process of dynamic dispatch.
Obviously, it will not be determined here based on the static type of the variable, because both are Human but have different behaviors. We checked the bytecode through javap and found that the parameters of the invokevirtual instruction in the two places are the same. The description is mainly that the specific implementation in the invokevirtual instruction is dynamically allocated. According to the "Java Virtual Machine Specification", the invokevirtual instruction parsing process is mainly divided into the following steps.

  1. Find the actual type of the object pointed to by the first element on the top of the operand stack , and mark it as O.
  2. If a method that matches the descriptor and simple name in the constant is found in O, then the access permission verification will be performed for a long time. If it is passed, the direct reference of this method will be returned, and an error will be reported if it fails.
  3. Otherwise, in accordance with the inheritance relationship, the second step of the operation is performed on the parent class in turn (in order to improve performance, the JVM uses a virtual method table in the method area of ​​the class to achieve this, and the initialization starts in the link phase.)
  4. If you can't find it, report an error.
    Note: There are only virtual methods in Java, and fields are not virtual, which means that fields will never participate in polymorphism. When the subclass declares a field with the same name as the parent class, although both fields will exist in the memory of the subclass, the field of the subclass will mask the field with the same name of the parent class.

Take a look at the following "bad interview questions"

public class TestClass {
    
    

    static  class Father{
    
    
        public int money = 1;

        public Father() {
    
    
            this.money = 2;
            showMoney();
        }

        protected  void showMoney(){
    
    
            System.out.println("I am Father , I hava $"+money);
        }
    }

    static  class Son extends Father{
    
    
        public int money = 3;

        public Son() {
    
    
            this.money = 4;
            showMoney();
        }

        protected  void showMoney(){
    
    
            System.out.println("I am Son , I hava $"+money);
        }
    }
    public static void main(String[] args) {
    
    
        Father guy = new Son();
        System.out.println("This guy has $"+guy.money);
    }

}
//输出
/*
I am Son , I hava $0
I am Son , I hava $4
This guy has $2
*/

Explanation:
First of all, because the process of creating Son will first call the constructor method of the parent class to initialize the field information of the parent class. At this time, it will run the constructor method of the parent class, set the money of the parent class to 2 and call showMoney(). At this time, the virtual method is called, so the output of Son is called instead of Father's output: I am Son, I hava $0 (Because the son constructor has not been executed yet, it is 0). After executing Son’s constructor method output: I am Son, I hava $4. The last call is because the field does not support polymorphism, so the call to guy.money is based on the static type of the variable to find money. Output: This guy has $2

Guess you like

Origin blog.csdn.net/null_zhouximin/article/details/112628519