[JVM] 04 virtual machine stack

1. Background of virtual machine stack

Q1 why is the execution of jvm instructions based on stack structure?

  • In order to achieve cross platform features
    Because of the register based structure and different CPU architectures on different platforms, cross platform characteristics cannot be realized. Cross platform can be realized based on stack.

Q2 advantages and disadvantages of designing instruction execution engine based on stack structure?

  • The advantages are cross platform, small instruction set and easy implementation of compiler
  • The disadvantage is performance degradation, and more instructions are required to achieve the same function.

2. Heap and stack in memory

Q1 how to understand that stack is the unit of runtime and heap is the unit of storage?

  • Stack solves the problem of program running, that is, how the program executes, or how to process data.
  • Heap solves the problem of data storage, that is, how and where to put the data.

3. Virtual machine stack

(1) What is a virtual machine stack

  • Java Virtual Machine Stack, also known as Java stack
  • Each thread will create a virtual machine stack when it is created, which is unique to the thread
  • Stack frames are stored inside

(2) Role of virtual machine stack

  • In charge of running Java programs
  • It saves the local variables of the method (8 basic data types, reference addresses of objects) and some results, and participates in the call and return of the method.
1.Local variable, which is compared to member variable (or attribute)
2.Basic data type variable VS Reference type variables (classes, arrays, interfaces)

(3) Characteristics of Java virtual machine stack

  • The life cycle is consistent with the thread, that is, when the thread ends, the virtual machine stack is destroyed
  • Stack is a fast and effective way to allocate storage, and its access speed is second only to program counter.
  • There are only two direct operations of the JVM on the Java stack:
    1. The execution of each method is accompanied by stack entry (stack entry and stack pressing)
    2. Stack out after execution
  • There is no garbage collection problem, but there must be an OOM exception

(4) Possible exceptions in the stack

  • Premise: the Java virtual machine specification allows the size of the Java stack to be dynamic or fixed

  • Case1: if a fixed size Java virtual machine stack is used
    The Java virtual machine stack capacity of each thread can be selected independently when the thread is created. If the allocated stack capacity requested by the thread exceeds the maximum capacity allowed by the Java virtual machine stack, the Java virtual machine will throw a StackOverFlowError exception [virtual machine stack size > java virtual machine stack Max]

  • Case2: if the java virtual machine stack can be expanded dynamically
    If enough memory cannot be requested when trying to expand, or there is not enough memory to create the corresponding virtual machine stack when creating a new thread, the java virtual machine will throw an OutOfMemoryError exception [the virtual machine stack size cannot be met]

  • Stack exception presentation

public class StackErrorTest {
    private static int count = 1;
    public static void main(String[] args) {
        System.out.println(count);
        count++;
        main(args);
    }
}

After a certain number of calls:

(5) Sets the memory size of the stack

-Xss

  • We can use the parameter - Xss option to set the maximum stack space of the thread. The size of the stack directly determines the maximum reachable depth of the function call.
-Xss1024m		// Stack memory is 1024MBS
-Xss1024k		// Stack memory is 1024KB
  • IDEA setting method:
Run->EditConfigurations->VM options Fill in the size of the specified stack-Xss256k

  • Modify stack size test
    Execute again:
public class StackErrorTest {
    private static int count = 1;
    public static void main(String[] args) {
        System.out.println(count);
        count++;
        main(args);
    }
}

The stack overflowed when it was printed to 2461

4. Storage structure and operation principle of virtual machine stack

4.1 operation principle of virtual machine stack


1. The units in the virtual machine stack are stack frames
2. Each method being executed on this thread corresponds to a Stack Frame.
3. Stack frame is a memory block and a data set, which maintains various data information in the process of method execution

1. There are only two direct operations of the JVM on the Java stack, that is, pressing and exiting the stack frame, following the first in first out (LIFO) principle
2. In an active thread, there will only be one active stack frame at a point in time. That is, only the stack frame (stack top stack frame) of the currently executing method is valid

  • This stack frame is called the current stack frame
  • The method corresponding to the current stack frame is the Current Method
  • The class that defines this method is the Current Class

3. All bytecode instructions run by the execution engine only operate on the current stack frame.
4. if other methods are invoked in the method, the corresponding new stack frame will be created and placed on the top of the stack to become a new current frame.
5. Stack frames contained in different threads are not allowed to refer to each other, that is, it is impossible to refer to the stack frame of another thread in one stack frame.
6. If the current method is called by other methods, when the method returns, the current stack frame will return the execution result of this method to the previous stack frame. Then, the virtual opportunity discards the current stack frame so that the previous stack frame becomes the current stack frame again.
7. There are two ways for Java methods to return functions, but no matter which method is used, the stack frame will pop up:

  • One is a normal function return, using the return instruction
  • One is to throw an exception

Code demonstration:

public class StackFrameTest {
    public static void main(String[] args) {
        StackFrameTest test = new StackFrameTest();
        test.method1();
    }

    public void method1() {
        System.out.println("method1()Start execution...");
        method2();
        System.out.println("method1()end of execution...");
    }

    public int method2() {
        System.out.println("method2()Start execution...");
        int i = 10;
        int m = (int) method3();
        System.out.println("method2()Coming to an end...");
        return i + m;
    }

    public double method3() {
        System.out.println("method3()Start execution...");
        double j = 20.0;
        System.out.println("method3()Coming to an end...");
        return j;
    }
}
method1()Start execution...
method2()Start execution...
method3()Start execution...
method3()Coming to an end...
method2()Coming to an end...
method1()end of execution...

4.2 stack frame

Stack frame internal structure

Each stack frame stores:

  1. Local Variables
  2. Operand Stack (or expression stack)
  3. Dynamic Linking (or method reference to the runtime constant pool)
  4. Method Return Address (or definition of normal or abnormal exit of the method)
  5. Some additional information

    Each thread has its own stack, and there are many stack frames in each stack. The size of stack frames is mainly determined by the local variable table and operand stack

1. Local Variables

  • Local variable table is also called local variable array or local variable table. The bottom layer is an array

  • Storage content
    Storage: method parameters, local variables
    These data types include various basic data types, object reference s, and returnAddress types

  • Local variables are defined in the method, and the local variable table refers to all local variables in the stack frame. The types of local variables include: 8 basic data types and object references; If it is a basic data type, it stores the value. If it is a reference type, it stores the object address.

  • Thread safety
    The local variable table is built on the thread stack and is thread private data. There is no data security problem

  • size
    The required capacity of the local variable table is determined during compilation and saved in the maximum local variables data item of the Code attribute of the Class file method. The size of the local variable table will not be changed during the operation of the method

  • The number of nested method calls is determined by the size of the stack. Generally speaking, the larger the stack, the more nested method calls. For a function, the more its parameters and local variables, the expansion of the local variable table, and the larger its stack frame, so as to meet the needs of increasing the information required for method calls. In turn, function calls will take up more stack space.

  • Scope and life cycle of local variable table
    Variables in the local variable table are valid only in the current method call.
    When the method is executed, the virtual machine completes the transfer process from the parameter value to the parameter variable list by using the local variable table; When the method call ends, the local variable table will be destroyed with the destruction of the method stack frame.

Experiment: view local variable table

import java.util.Date;

public class LocalVariablesTest {
    private int count = 0;

    public static void main(String[] args) {
        LocalVariablesTest test = new LocalVariablesTest();
        int num = 10;
        test.test1();
    }

    public void test1() {
        Date date = new Date();
        String name1 = "atguigu.com";
        test2(date, name1);
        System.out.println(date + name1);
    }

    public String test2(Date dateP, String name2) {
        dateP = null;
        name2 = "songhongkang";
        double weight = 130.5;//Occupy two slot s
        char gender = 'male';
        return dateP + name2;
    }

}

Javap view bytecode file:

You can also install the jclasslib byte viewcoder plug-in on the IDEA to view the internal bytecode information of the method:

After decompilation, it can be concluded that:

  • During compilation, the number of local variables and the size of each local variable have been recorded
  • Therefore, the required capacity of the local variable table is determined at compile time

(1) View method declaration information in bytecode

[Ljava/lang/String] :

  • [] represents an array
  • L indicates the reference type
  • java/lang/String represents java.lang.String

(2) The bytecode length of the view method is 16 (0 ~ 15)

(3) Method exception information table

(4) Miscellaneous (Misc)

(5) Correspondence between bytecode instruction line number and original java code line number

(6) The number of effective lines and the number of remaining effective lines are the number of lines for the bytecode file

The information in LocalVariableTable is:

  1. Number, name and type of local variables of the method
  2. Life cycle of local variables:
    startPc: which bytecode instruction does the local variable start from
    Length: the length of the local variable in the bytecode instruction
  3. Symbol reference
    Green underlined: is the symbolic reference of the local variable in the runtime constant pool

1.1. Understanding and demonstration of variable slot

1. For the local variable table, the most basic storage unit is slot
2. Parameter values are always stored from index=0 of local variable array to index of array length - 1
3. The local variable table stores variables of various basic data types (8 types), reference types and returnAddress types known during compilation.
4. In the local variable table, types within 32 bits only occupy one slot (including reference type and returnAddress type), and 64 bit types (long and double) occupy two slots.

byte,short,char,float Converted to before storage int
boolean Also converted to int,0 express false,Non-zero indicates true;

5. The JVM will assign an access index to each slot in the local variable table, and the local variable value specified in the local variable table can be successfully accessed through this index

6. When an instance method is called, its method parameters and local variables defined inside the method will be copied to each slot in the local variable table according to the declaration order
7. If you need to access a 64 bit local variable value in the local variable table, you only need to use the previous index. (for example, the weight variable in the above figure occupies two slot s, and its index value is 2)
8. If the method corresponding to the current frame is a construction method or a non static method, the object reference this will be stored in the slot with index 0, and the other parameters will be arranged in the order of the parameter table.

9. This cannot be referenced in the static method because this does not exist in the local variable table in the stack frame corresponding to the static method. So static methods cannot use this and super

public class LocalVariablesTest {

    private int count = 1;
    //Static methods cannot use this
    public static void testStatic(){
        //Compilation error, because this variable does not exist in the local variable table of the current method!!!
        System.out.println(this.count);
    }
}

1.2 reuse of slots

  • The slots in the local variable table in the stack frame can be reused. If a local variable passes its scope, the new local variable declared after its scope is likely to reuse the slots of expired local variables, so as to save resources
private void test3() {
        int a = 0;
        {
            int b = 0;
            b = a+1;
        }
        //The slot position occupied by variable b before and after the use of variable c
        int c = a+1;
}


  • Only 3 slot s are used;
  • this occupies slot 0, a occupies slot 1, b occupies slot 2, and c reuses slot 2
  • Since the termination pc of b is 8 and the starting pc of c is 12, c can reuse slot 2

1.3 comparison between static variables and local variables

1. Classification of variables

By data type

  1. Basic data type;
  2. Reference data type;

According to the position declared in the class
1. Member variables

  • Member variables have undergone default initialization and assignment before use
  • Stati c modified member variable (class variable): in the class loading phase, the default value is assigned to the class variable in the preparation phase of the second process link, and then the class variable is explicitly assigned in the initialization phase of the class loading phase;
  • Member variable not modified by static (instance variable): with the creation of the object, the instance variable space will be allocated in the heap space and assigned by default

2. Local variables

  • Explicit assignment must be performed before use! Otherwise, the compilation fails
  • Unlike member variable initialization, there is no system initialization process for local variable table, which means that once local variables are defined, they must be initialized manually, otherwise they cannot be used.

1.4 supplementary instructions

1. Performance tuning of stack frames
In the stack frame, the part most closely related to performance tuning is the local variable table. When the method is executed, the virtual machine uses the local variable table to complete the transfer of the method

2. The variables in the local variable table are also GC Root
Variables in the local variable table are also important garbage collection root nodes (GC Roots). As long as the objects directly or indirectly referenced in the local variable table are not recycled.

2. Operand Stack

  • In addition to the local variable table, each independent stack frame also contains a Last - In - First -Out operand stack, which can also be called Expression Stack
  • Operand stack: in the process of method execution, data is written or extracted into the stack according to bytecode instructions, that is, push and pop
  • -Some bytecode instructions push values into the operand stack, while others take operands out of the stack. After using them, the results are pushed onto the stack, such as copying, exchanging, summing, etc

2.1 characteristics of operand stack

  • The operand stack is mainly used to save the intermediate results of the calculation process

  • The operand stack is a working area of the jvm execution engine. When a method starts executing, a new stack frame will be created. The operand stack of this method is empty

  • Each operand stack will have an explicit stack depth for storing values. The maximum depth required is defined by the compiler and saved in the code attribute of the method in the class file, which is max_ Value of stack

  • Any element in the stack can be any java data type
    ① 32bit type occupies a stack unit depth
    ② The 64bit type occupies two stack unit depths

  • The operand stack does not access the data by accessing the index, but can only access the data once through the standard push and pop operations

  • If the called method has a return value, its return value will be pushed into the operand stack of the current stack frame, and the next bytecode instruction to be executed in the PC register will be updated.

  • The data type of the element in the operand stack must strictly match the bytecode instruction, which is verified by the compiler during compilation. At the same time, it is verified again in the data flow analysis phase of the class verification phase during class loading.

  • In addition, we say that the interpretation engine of Java virtual machine is a stack based execution engine, in which the stack refers to the operand stack.

2.2 operand stack code demonstration

2.2.1 code example
public class testAddOperation {
    public void testAddOperation() {
        //byte, short, char, boolean: all are saved in int type
        byte i = 15;
        int j = 8;
        int k = i + j;
    }

    public static void main(String[] args) {
        testAddOperation tao = new testAddOperation();
        tao.testAddOperation();
    }
}	 

Bytecode instruction information:

 public void testAddOperation();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=1
         0: bipush        15
         2: istore_1
         3: bipush        8
         5: istore_2
         6: iload_1
         7: iload_2
         8: iadd
         9: istore_3
        10: return
      LineNumberTable:
        line 4: 0
        line 5: 3
        line 6: 6
        line 7: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   LtestAddOperation;
            3       8     1     i   B
            6       5     2     j   I
           10       1     3     k   I

Operand stack code trace:

explain

  • First, a method starts to execute, and the local variable table, operand stack and pc counter of its corresponding stack frame will be initialized. Then start to operate in the order of bytecode instructions;

  • Bytecode instruction description:
    1.push: stack
    2.store: store in local variable table
    3.load: take the data in the slot from the local variable table and put it into the operand stack

  • The prefix of bytecode instruction indicates the data type of operand:
    bipush: push operand stack bi represents byte type data
    istore: store the top element of the operand stack into the local variable table. i represents int type data
    (the seventh feature of the operand stack is verified here: the data type of the elements in the operand stack must strictly match the bytecode instruction)

  • Suffix of bytecode instruction
    Bytecode instructions related to local variables will have additional slot information in the suffix

After describing the bytecode, let's take a look at the execution process of a method (stack frame):

1. The first statement 0 bipush 15

  • The PC register points to 0, that is, the instruction address is 0
  • Then use bipush to put the byte operand 15 into the operand stack.

    After execution, let PC + 1 point to the next line of code

2. The second statement 2 iStore_ one

  • Store the elements of the operand stack in the local variable table where the index is 1. We can see that an element has been added to the local variable table
    Explain why the local variable table index starts from 1, because this method is an instance method, and this is stored where the local variable table index is 0

3. The third and fourth statements 4 bipush 8 and 5 iStore_ two

  • Put operand 8 on the stack
  • Then it is stored in slot 2 of the local variable table

4.6:iload_1 and 7: Iload_ two

  • From the local variable table, select slot in turn_ 1 and slot_ The data in 2 is placed in the operand stack and waiting for the add operation


5.iadd and iStore_ three

  • Adds two elements in the operand stack
  • The results are stored in local variable table 3

About int j = 8; Description of

The bytecode instructions we decompile are as follows

  • Because 8 can be stored in byte type, the type of pushed operand stack is byte instead of int, so the bytecode instruction executed is bipush 8
  • Then execute to store the value 8 in a variable of type int: iStore_ two
2.2.2 stack of method return value
public int getSum(){
    int m = 10;
    int n = 20;
    int k = m + n;
    return k;
}

public void testGetSum(){
    //Get the result returned by the previous stack frame and save it in the operand stack
    int i = getSum();
    int j = 10;
}
  • Byte code instruction of getSum(): with an ireturn at the end:

  • testGetSum() bytecode instruction: load the return value of getSum() method immediately:

2.2.3 differences between + + i and i + +
public void add(){
    //Category 1 questions:
    int i1 = 10;
    i1++;

    int i2 = 10;
    ++i2;

    //Type 2 questions:
    int i3 = 10;
    int i4 = i3++;

    int i5 = 10;
    int i6 = ++i5;

    //Category 3 questions:
    int i7 = 10;
    i7 = i7++;

    int i8 = 10;
    i8 = ++i8;

    //Category 4 questions:
    int i9 = 10;
    int i10 = i9++ + ++i9;
}

i++

//Type 2 questions:
int i3 = 10;
int i4 = i3++;

Bytecode instruction:

  • bipush 10: push 10 into operand stack
  • istore_3: Save the 10 in the operand stack to the variable i3
  • iload_3: Load the value (10) of variable i3 into the operand stack
  • iinc 3 by 1: the variable i3 performs the + 1 operation
  • istore 4: save the value in the operand stack to the variable i4 (10)

++i

int i5 = 10;
int i6 = ++i5;

Bytecode instruction:

  • bipush 10: push 10 into operand stack
  • istore 5: save 10 in the operand stack to variable i5
  • iinc 5 by 1: the variable i5 performs the + 1 operation
  • iload 5: load the value (11) of variable i5 into the operand stack
  • istore 6: save the value in the operand stack to the variable i6 (11)

Summary:
i + +: first load the value of i into the operand stack, and then add 1 to the value of i
++i: First increase the value of I by 1, and then load the value of I into the operand stack

2.3 top of stack caching (ToS)

  • Advantages and disadvantages: the zero address instructions used by the virtual machine based on the stack architecture (i.e. simply putting in and out of the stack without considering the address) are more compact, but more putting in and out of the stack instructions must be used to complete an operation, which also means that more instruction dispatch times and memory read / write times will be required
  • Since operands are stored in memory, frequent memory read / write operations will inevitably affect the execution speed. In order to solve this problem, the designers of HotSpot JVM proposed the stack top cache technology, which caches all the stack top elements in the registers of the physical CPU, so as to reduce the number of reads / writes to the memory and improve the execution efficiency of the execution engine

3. Dynamic linking

  • Each stack frame contains a dynamic link [that is, a reference to the method to which the stack frame belongs in the runtime constant pool (therefore, it can be understood why only methods in java are polymorphic and attributes are not polymorphic)]

  • When Java source files are compiled into bytecode files, all variable and method references are saved in the constant pool of class files as symbolic references.

  • From the above figure, we can see that there are several references to the runtime constant pool, such as class reference and field reference; But dynamic links refer to method references

  • The purpose of including this reference in the stack frame is to support the current method to realize dynamic linking, such as invokedynamic instruction

  • For example, when a method calls another method, it is represented by the symbolic references to the method in the constant pool. The function of dynamic link is to convert these symbolic references into direct references to the calling method

Code example

public class DynamicLinkingTest {
    int num = 10;

    public void methodA(){
        System.out.println("methodA()....");
    }

    public void methodB(){
        System.out.println("methodB()....");
        methodA();
        num++;
    }
 
}


In bytecode instruction, method A is called through invokevirtual #7 instruction in methodB() method, so #7 what is it? Constant pool not found:

Conclusion: through #7 us, we can find the methodA() method to be called and call it

Why do I need a constant pool?

1. Because constants or methods can be called in different methods, you only need to store one copy and record its reference, which saves space
2. The function of constant pool is to provide some symbols and constants to facilitate instruction recognition

3.1 analysis of dynamic links

3.1.1 static link mechanism and dynamic link mechanism
  • In the JVM, converting a symbolic reference to a direct reference of a calling method is related to the binding mechanism of the method. Linking refers to the process of converting a symbolic reference to a direct reference.

1. Static link

  • When a bytecode file is loaded into the JVM, if the called target method is determined at compile time and remains unchanged at run time, the process of converting the symbolic reference of the calling method into a direct reference is called static link

2. Dynamic link

  • If the called method cannot be determined at the compile time, that is, the symbol of the called method can only be converted into a direct reference at the program run time. Because this reference conversion process is dynamic, it is also called dynamic link.
3.1.2 early binding and late binding
  • The binding mechanisms of methods corresponding to static links and dynamic links are early binding and late binding
  • Binding refers to the process of replacing class reference, field reference and method reference in the constant pool with direct reference, which occurs only once.

1. Early binding
Early binding means that if the called target method is known at the compile time and remains unchanged at the run time, the method can be bound with the class to which the method belongs. In this way, since it is clear which target method is called, the symbolic reference can be converted into a direct reference by means of static link.

2. Late binding
If the called method cannot be determined at compile time, it can only bind the relevant method according to the actual type at program run time. This binding method is also called late binding.

3. Code example

/**
 * Illustrate examples of early binding and late binding
 *
 * @author shkstart
 * @create 2020 11:59 am
 */
class Animal {
    public void eat() {
        System.out.println("Animal feeding");
    }
}

interface Huntable {
    void hunt();
}

class Dog extends Animal implements Huntable {
    @Override
    public void eat() {
        System.out.println("Dogs eat bones");
    }

    @Override
    public void hunt() {
        System.out.println("Prey on mice and mind your own business");
    }
}

class Cat extends Animal implements Huntable {
    public Cat() {
        super();//Performance: early binding
    }

    public Cat(String name) {
        this();//Performance: early binding
    }

    @Override
    public void eat() {
        super.eat();//Performance: early binding
        System.out.println("Cats eat fish");
    }

    @Override
    public void hunt() {
        System.out.println("It's natural to prey on mice");
    }
}

public class AnimalTest {
    public void showAnimal(Animal animal) {
        animal.eat();//Performance: late binding
    }

    public void showHunt(Huntable h) {
        h.hunt();//Performance: late binding
    }
}
  • invokevirtual is embodied as late binding
  • invokeinterface is also embodied as late binding
  • invokespecial is embodied as early binding
3.1.3 polymorphism and method binding mechanism
  • With the emergence of high-level languages, there are more and more object-oriented programming languages similar to Java. Although these programming languages have certain differences in syntax style, they always maintain a common feature with each other, that is, they all support object-oriented features such as encapsulation, inheritance and polymorphism. Since these programming languages have polymorphism, Naturally, there are two binding methods: early binding and late binding.
  • Any ordinary method in Java actually has the characteristics of virtual functions. They are equivalent to virtual functions in C + + language (in C + +, the keyword virtual needs to be explicitly defined). If you do not want a method to have the characteristics of a virtual function in a java program, you can use the keyword final to mark the method (that is, when the method cannot be overridden by an inherited subclass, it does not have the characteristics of a virtual function)
3.1.4 virtual and non virtual methods

1. Difference between virtual method and non virtual method

  • If the method determines the specific call version at compile time, this version is immutable at run time. Such a method is called non virtual method.
  • Static methods, private methods, final methods, constructors (this(...)), and parent methods (super(...)) are all non virtual methods.
  • Other methods are called virtual methods

It can be understood that methods that cannot be inherited and overridden are non virtual methods, because non virtual methods cannot reflect polymorphism. All other methods that can embody polymorphism are called virtual methods

2. Preconditions for the use of polymorphism of subclass objects

  • Class inheritance
  • Method override

The interface used in the actual development and code writing is the function implemented by the imported third-party jar package

3. The following method call instructions are provided in the virtual machine

4 ordinary call instructions:

  • invokestatic: call static methods, and determine the unique method version in the parsing phase;
  • invokespecial: call < init > method, private method and parent method; Determine the unique method version in the parsing phase;
  • invokevirtual: call all virtual methods;
  • invokeinterface: call the interface method;

1 dynamic call instruction (new in Java7):

  • invokedynamic: dynamically resolve the method to be called, and then execute it

difference:

  1. The first four instructions are solidified in the virtual machine, and the method invocation and execution cannot be interfered by human beings, while the invokedynamic instruction supports the user to determine the method version
  2. The methods called by the invokestatic instruction and invokespecial instruction are called non virtual methods, and the rest (except those modified by final) are called virtual methods.
  3. The final method belongs to the invokevirtual instruction by the JVM, but the final modification is also a non virtual method, which should be noted here.
/**
 * Testing of non virtual methods and virtual methods in parsing calls
 *
 * invokestatic The methods called by the instruction and invokespecial instruction are called non virtual methods
 * @author shkstart
 * @create 2020 12:07 PM
 */
class Father {
    public Father() {
        System.out.println("father Constructor for");
    }

    public static void showStatic(String str) {
        System.out.println("father " + str);
    }

    public final void showFinal() {
        System.out.println("father show final");
    }

    public void showCommon() {
        System.out.println("father Common method");
    }
}

public class Son extends Father {
    public Son() {
        //invokespecial
        super();
    }

    public Son(int age) {
        //invokespecial
        this();
    }

    //Is not a static method of overridden parent class, because static methods cannot be overridden!
    public static void showStatic(String str) {
        System.out.println("son " + str);
    }

    private void showPrivate(String str) {
        System.out.println("son private" + str);
    }

    public void show() {
        //invokestatic
        showStatic("atguigu.com");

        //invokestatic
        super.showStatic("good!");

        //invokespecial
        showPrivate("hello!");

        //invokevirtual
        //Although the bytecode instruction is shown as invokevirtual, this method is also considered a non virtual method because it declares final and cannot be overridden by subclasses.
        showFinal();

        //invokespecial
        super.showCommon();

        //invokevirtual
        //It is possible that the subclass will override the showCommon() method of the parent class
        showCommon();
        info();

        MethodInterface in = null;
        //invokeinterface
        in.methodA();
    }

    public void info() {

    }

    public void display(Father f) {
        f.showCommon();
    }

    public static void main(String[] args) {
        Son so = new Son();
        so.show();
    }
}

interface MethodInterface {
    void methodA();
}

Reference bytecode file:

About invokedynamic directive

  • The JVM bytecode instruction set has been relatively stable until java7 added an invokedynamic instruction, which is an improvement made by Java to realize the support of [dynamic type language]
  • However, java 7 does not provide a method to directly generate invokedynamic instructions, so it is necessary to generate invokedynamic instructions with the help of ASM, a low-level bytecode tool. Until the emergence of Lambda expression in java 8, the generation of invokedynamic instructions did not have a direct generation method in java
  • The essence of the dynamic language type support added in java 7 is to modify the java virtual machine specification, not the java language rules. This part is relatively complex and adds method calls in the virtual machine. The most direct beneficiary is the dynamic language compiler running on the java platform
3.1.5 dynamically typed languages and statically typed languages
  • The difference between dynamic type language and static type language lies in whether the type check is at compile time or at run time. The former is static type language, and the other is dynamic type language.
  • To put it bluntly, static language is the type information to judge the variable itself; Dynamic type language is the type information to judge the value of variables. Variables have no type information, and variable values have type information, which is an important feature of dynamic language
  • Java is a statically typed language (although lambda expressions add dynamic features to it), js and python are dynamically typed languages
Java:  String info = "silicon valley";//Static language
JS:    var name = "Silicon Valley "; var name = 10;//Dynamic language
Pythom: info = 130;//More thorough dynamic language

3.2 dynamic link assignment

3.2.1 nature of method rewriting (dispatch)

1. Find the actual type of the object executed by the first element of the operand stack and record it as C.
2. If a method matching the descriptor and simple name in the constant pool is found in type C, the access permission is verified. If it passes, the direct reference of the method is returned and the search process is ended; if it fails, the java.lang.IllegalAccessError exception is returned.
3. Otherwise, perform the second step of searching and verifying each parent class of c from bottom to top according to the inheritance relationship.
4. If no suitable method is found, a java.lang.AbstractMethodError exception will be thrown.

  • Introduction to IllegalAccessError
    The program view accesses or modifies a property or calls a method. You do not have permission to access this property or method.
    Generally, this will cause compiler exceptions. If this error occurs at runtime, it indicates that an incompatible change has taken place in a class. For example, you removed the jar package from the project, or there is a jar package conflict in Maven

  • Look back at the parsing phase
    1. The parsing stage is the process of converting symbolic references in the constant pool into direct references (parsing stage = dispatch process)
    2. The parsing action is mainly for class or interface, field, class method, interface method, method type, etc. it corresponds to CONSTANT Class info, CONSTANT Fieldref info, CONSTANT Methodref info, etc. in the constant pool

3.2.2 virtual method table

a. Function of virtual method table

  • In object-oriented programming, dynamic dispatch is frequently used. If you need to search the appropriate target in the method metadata of the class again during each dynamic dispatch, it may affect the execution efficiency. Therefore, in order to improve performance, the jvm establishes a virtual method table in the method area of the class (non virtual methods will not appear in the table) Use an index table instead of a lookup.
Translation:
hypothesis A Method calls a bunch of methods, but because we don't know whether these methods are virtual methods,
So we should follow『assignment』Process to find the real class of the method. Due to the low search efficiency,
Thus, when A A virtual square is established in the method area of the class where the method is located,
By checking the table to determine whether it is a virtual method, the process of searching the class to which this method belongs in the constant pool is omitted.

There is a virtual method table in the method area of each class, which stores the actual entry of each method.

b. When is the virtual method table created?

The virtual method table will be created in the link phase of class loading (link parsing phase) and initialized. After the initial value of the class variable is prepared, the jvm will also initialize the virtual method table of the class.

c. Virtual method table instance

We define three classes and a Friendly interface

  • Virtual method table of Dog class
class Dog{
  public void sayHello(){
  }
  public String toString(){
    return "Dog";
  }
}


Conclusion 1: the virtual method table of the current class contains the virtual methods of the current class and the inherited virtual methods

  • Cocker dog deficiency method table
class CockerSpaniel extends Dog implements Friendly{
  public void sayHello(){
    super.sayHello();
  }
  public void sayGoodbye(){
  }
}


If you use the toString method, you don't need to look up the Object class, you just need to find the Dog class. This is an efficiency improvement
Conclusion 2: the inherited virtual method points to the nearest parent class

  • Virtual method table for cats:
interface Friendly{
  void sayHello();
  void sayGoodbye();
}

class Cat implements Friendly{
  public void eat(){
  }
  public void sayHello(){
  }
  public void sayGoodbye(){
  }
  protected void finalize(){
  }
  public String toString(){
  }
}

Conclusion 3: if the current class overrides the virtual method of the parent class, the virtual method points to itself

Conclusion:
(1) The parsing phase in the link of the virtual method to the real class is created and initialized
(2) This table is a collection of all virtual methods of this class. It is placed in the array and indicates whether this method belongs to itself or its parent class. Without dynamic allocation, you have to check the actual type of this method in the constant pool of the method area.
(3) If the class overrides the virtual method of the parent class, the virtual method points to itself

4. Method return address

1. Store the value of the pc register of the method calling the current method (the address of the next instruction of the instruction calling the method)

2. There are two ways to end a method:

  • Normal execution completed
  • Unhandled exception occurred, abnormal exit

No matter how you exit, you will return to the location where the method is called after the method exits.
When the method exits normally, take the value of the caller's pc counter as the return address;
In case of abnormal exit, the return address should be determined through the exception table. Generally, this part of information will not be saved in the stack frame.
The difference between the normal completion exit and the exception completion exit is that the exit through the exception completion exit will not produce any return value to its upper caller.

In essence, the method exit is the process of the current stack frame out of the stack. At this time, it is necessary to restore the local variable table and operand stack of the upper method, press the return value into the operand stack of the caller's stack frame, set the PC register value, etc., and let the caller's method continue to execute.

When a method starts executing, there are only two ways to exit the method

  • Normal exit:
    1. When the execution engine encounters a bytecode instruction (return) returned by any method, the return value will be passed to the upper method caller, referred to as the normal completion exit;
    2. After a method is called normally, which return instruction needs to be used depends on the actual data type of the method return value.
    In bytecode instructions, the return instructions include:
    (1)ireturn: used when the return values are boolean, byte, char, short, and int types
    (2)lreturn: Long type
    (3) Freeturn: Float type
    (4)dreturn: Double Type
    (5)areturn: reference type
    (6)return: the method whose return value type is void, the instance initialization method, and the class and interface initialization method

  • Abnormal exit
    1. An Exception is encountered during the execution of the method, and the Exception is not handled in the method, that is, as long as no matching Exception handler is found in the Exception table of the method, the method will exit, referred to as Exception completion exit
    2. The exception handling when an exception is thrown during method execution is stored in an exception handling table to facilitate finding the code to handle the exception when an exception occurs.
    Code example

public class ReturnAddressTest {
//ireturn
    public boolean methodBoolean() {
        return false;
    }
    //ireturn
    public byte methodByte() {
        return 0;
    }
    //ireturn
    public short methodShort() {
        return 0;
    }
    //ireturn
    public char methodChar() {
        return 'a';
    }
    //ireturn
    public int methodInt() {
        return 0;
    }
    //lreturn
    public long methodLong() {
        return 0L;
    }
//freturn
    public float methodFloat() {
        return 0.0f;
    }
    //dreturn
    public double methodDouble() {
        return 0.0;
    }
    //areturn
    public String methodString() {
        return null;
    }
     //areturn
    public Date methodDate() {
        return null;
    }
     //return
    public void methodVoid() {

    }

    static {
        int i = 10;
    }

    public void method2() {
        methodVoid();
        try {
            method1();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void method1() throws IOException {
        FileReader fis = new FileReader("atguigu.txt");
        char[] cBuffer = new char[1024];
        int len;
        while ((len = fis.read(cBuffer)) != -1) {
            String str = new String(cBuffer, 0, len);
            System.out.println(str);
        }
        fis.close();
    }
 
}
  • Method2() handles exceptions with try catch, and there is an exception handling table in Code

    Exception handling table:
    1. Decompile the bytecode file to get the Exception table
    2.from: start address of bytecode instruction
    3.to: end address of bytecode instruction
    4.target: if an exception occurs, jump to the instruction execution at address 11
    5.type: the type of exception caught

  • Method1() throws exceptions. There is no exception handling table in code

5. Interview questions related to virtual machine stack

5.1 what is an example of stack overflow? (StackOverflowError)

Recursive call;
Set the stack size through - Xss;

5.2 can adjusting the stack size ensure no overflow?

No, for example, the infinite number of recursions will definitely overflow. Adjusting the stack size can only ensure that the overflow time is later. The extreme situation will lead to out of memory error

5.3 is the larger the stack memory allocated, the better?

Does it occupy the space of other threads

5.4 does garbage collection involve virtual machine stack?

can't

5.5 is the local variable defined in the method thread safe?

What is thread safety?

  • If only one thread can manipulate this data, it must be thread safe.
  • If multiple threads operate on this data, it is shared data. If the synchronization mechanism is not considered, there will be thread safety problems.

Specific analysis of specific problems:

  • For the local variables in the method, they are the data in the stack frame and are thread private. If the object is generated internally and dies internally and does not return to the outside, it is thread safe. On the contrary, it is thread unsafe.
public class StringBuilderTest {
    //s1 is declared thread safe
    public static void method1(){
        //StringBuilder: thread unsafe
        StringBuilder s1 = new StringBuilder();
        s1.append("a");
        s1.append("b");
        //...
    }
    //There is a thread unsafe problem in the method passed by sBuilder through parameters
    public static void method2(StringBuilder sBuilder){
        sBuilder.append("a");
        sBuilder.append("b");
        //...
    }
    //After s1 is operated, s1 is returned as a return value. There is a thread unsafe problem
    public static StringBuilder method3(){
        StringBuilder s1 = new StringBuilder();
        s1.append("a");
        s1.append("b");
        return s1;
    }
    //s1 operation: thread safe
    public static String method4(){
        StringBuilder s1 = new StringBuilder();
        s1.append("a");
        s1.append("b");
        return s1.toString();
    }

    public static void main(String[] args) {
        StringBuilder s = new StringBuilder();
        new Thread(() -> {
            s.append("a");
            s.append("b");
        }).start();
        method2(s);
    }
}

5.6 in the runtime data area, which parts have errors and GC?

Tags: Java jvm

Posted on Sat, 20 Nov 2021 17:45:35 -0500 by b-real