Deeply understand the underlying principle of JVM - runtime data area

Keywords: Java

Runtime datazone overview and threads


1. Overview of runtime data area

!

     Memory is a very important system resource. It is the intermediate warehouse and bridge between hard disk and CPU. It carries the real-time operation of operating system and applications. The JVM memory layout specifies the strategies of memory application, allocation and management during Java operation, ensuring the efficient and stable operation of the JVM. Different JVMs have some differences in memory partition and management mechanism. Combined with the JVM virtual machine specification, let's discuss the classic JVM memory layout.


Java virtual machine defines several runtime data areas that will be used during program running, some of which will be created with the startup of the virtual machine and destroyed with the exit of the virtual machine. Others correspond to threads one by one. These data areas corresponding to threads will be created and destroyed as the thread starts and ends.
     Gray is private to a single thread, and red is shared by multiple threads. Namely:

  •   Each thread: independent, including program counter, stack and local stack.
  •   Inter thread sharing: heap, out of heap memory (permanent generation or meta space, code cache)


There is only one Runtime instance per JVM. That is, the Runtime environment, which is equivalent to the frame in the middle of the memory structure: the Runtime environment.

2. Thread

  • A thread is the running unit of a program. The JVM allows an application to have multiple threads executing in parallel.
  • In the Hotspot JVM, each thread is directly mapped to the local thread of the operating system.
  1. When a Java thread is ready to execute, a local thread of the operating system is also created. After Java thread execution terminates, the local thread will also be recycled.
  • The operating system is responsible for scheduling all threads to any available CPU. Once the local thread is initialized successfully, it calls the run() method in the Java thread.
  • If you use jconsole or any debugging tool, you can see many threads running in the background. These background threads do not include the main thread calling public static void main(String[] args) and all threads created by this main thread.
  • These main background system threads are mainly the following in the Hotspot JVM:
  1. Virtual machine thread: the operation of this thread requires the JVM to reach the safety point. These operations must occur in different threads because they all need the JVM to reach a safe point so that the heap will not change. The execution types of this thread include "stop the world" garbage collection, thread stack collection, thread suspension and partial lock revocation.
  2. Periodic task threads: these threads are the embodiment of periodic time (such as interrupt). They are generally used for scheduling and execution of periodic operations.
  3. GC thread: this thread supports different kinds of garbage collection behavior in the JVM.
  4. Compilation thread: this thread will compile bytecode into local code at run time.
  5. Signal scheduling thread: this thread receives signals and sends them to the JVM, which processes them internally by calling appropriate methods.

3. Program counter (PC register)


3.1 introduction to PC register



In the Program Counter Register in the JVM, the name of the Register comes from the CPU Register, which stores field information related to instructions. The CPU can run only when it loads data into registers.
Here, it is not a physical register in a broad sense. Perhaps it will be more appropriate to translate it into PC counter (or instruction counter) (also known as program hook), and it is not easy to cause some unnecessary misunderstandings. The PC register in the JVM is a kind of idle simulation of the physical PC register.

Function: PC register is used to store the address pointing to the next instruction and the instruction code to be executed. The next instruction is read by the execution engine.

  • It is a small memory space that can be almost ignored. It is also the fastest storage area.
  • In the JVM specification, each thread has its own program counter, which is thread private, and the life cycle is consistent with the thread life cycle.
  • At any time, a thread has only one method executing, that is, the so-called current method. The program counter will store the JVM instruction address of the Java method being executed by the current thread; Or, if the native method is being executed, the value is unspecified.
  • It is the indicator of program control flow. The basic functions such as branch, loop, jump, exception handling and thread recovery need to be completed by this counter.
  • The bytecode interpreter works by changing the value of this counter to select the next bytecode instruction to be executed.
  • It is the only area where no OutOfMemoryError condition is specified in the Java virtual machine specification.

3.2 example of using PC register



 

public class PCRegisterTest {
    public static void main(String[] args) {
        int i = 10;
        int j = 20;
        int k = i + j;
        String s = "abc";
        System.out.println(i);
        System.out.println(k);
    }
}

3.3 two common problems of PC register



What's the use of using PC registers to store bytecode instruction addresses?
Why use the PC register to record the execution address of the current thread?
Because the CPU needs to constantly switch threads, after switching back, you have to know where to continue execution.
The bytecode interpreter of the JVM needs to change the value of the pc register to determine what bytecode instruction should be executed next.

  Why is the PC register set to thread private?
We all know that the so-called multithreading method will only execute one thread in a specific time period, and the CPU will keep switching tasks, which will inevitably lead to frequent interruption or recovery. How to ensure no difference? In order to accurately record the current bytecode instruction address being executed by each thread, the best way is to allocate a pc register for each thread, so that each thread can calculate independently, so as not to interfere with each other.
Due to the rotation limitation of CPU time slice, during the concurrent execution of many threads, at any given time, a processor or a kernel in a multi-core processor will only execute one instruction in a thread.
This will inevitably lead to frequent interruption or recovery. How to ensure no difference? After each thread is created, it will generate its own program counter and stack frame. The program counter does not affect each other among threads.

CPU time slice and the time allocated by the CPU to each program. Each thread is allocated a time period, which is called its time slice.
At the macro level: we can open multiple applications, and each program runs side by side at the same time.
But at the micro level: because there is only one CPU, it can only process part of the program requirements at a time. One way to deal with fairness is to introduce time slices, and each program is executed in turn.

4. Virtual machine stack


4.1 overview of virtual machine stack

Background of virtual machine stack
     Due to the cross platform design, Java instructions are designed according to the stack. Different platforms have different CPU architectures, so they cannot be designed as register based.
     The advantages are cross platform, small instruction set and easy implementation of the compiler. The disadvantages are performance degradation and more instructions are required to realize the same function.
     When many Java developers mention the JAVA memory structure, they will understand the memory area in the JVM as only Java heap and Java stack? Why?
     Stack and heap in memory
     Stack is the unit of runtime, and heap is the unit of storage.
     That is, stack solves the problem of program operation, that is, how to execute the program, or how to process data. Heap solves the problem of data storage, that is, how and where to store data.



 

4.2 basic contents of virtual machine stack

  • Java virtual Machine Stack (Java virtual Machine Stack), also known as Java stack. When each thread is created, it will create a virtual machine stack, which stores stack frames one by one, corresponding to this Java method call( (thread private)
  • Life cycle: the life cycle is consistent with the thread
  • Function: in charge of the operation of Java programs, it saves local variables of methods (basic data types and reference addresses of objects in 8) and partial results, and participates in the call and return of methods( Local variable vs member variable - attribute; Basic data type variable vs reference data type variable (class, array, interface)
package com.atguigu.java;

/**
 * @author shkstart
 * @create 2020 8:32 PM
 */
public class StackTest {

    public static void main(String[] args) {
        StackTest test = new StackTest();
        test.methodA();
    }

    public void methodA() {
        int i = 10;
        int j = 20;

        methodB();
    }

    public void methodB(){
        int k = 30;
        int m = 40;
    }
}

Stack features (advantages):

  • Stack is a fast and effective way to allocate storage, and its access speed is second only to program counter
  • There are only two operations for JVM direct heap Java stack:
    • Each method executes, along with the stack in (stack in and stack out)
    • Stack out after execution
  • There is no garbage collection problem for the stack

  Possible exceptions in stack:

  •   The Java virtual machine specification allows the size of the Java stack to be dynamic or fixed.
    1. 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 thread requests to allocate more stack capacity than the maximum allowed by the Java virtual machine stack, the Java virtual machine will throw a StackOverflowError exception.
    2. If the Java virtual machine stack can be expanded dynamically and cannot apply for enough memory when trying to expand, or there is not enough memory area to create the corresponding virtual machine stack when creating a new thread, the Java virtual machine will throw OutOfMemoryError exception.

Example: demonstrate exceptions in the stack


/**
 * Demonstrate the exception in the stack: StackOverflowError
 * @author shkstart
 * @create 2020 9:08 PM
 *
 *  By default: Count: 11323
 *  Set stack size: - xss256k: Count: 2461
 */
public class StackErrorTest {
    private static int count = 1;
    public static void main(String[] args) {
        System.out.println(count);
        count++;
        main(args);
    }

}

Set stack memory size:

We can use the - Xss option to set the maximum thread space. The stack size directly determines the maximum depth of function calls.

 

 
4.3 storage unit of stack

What is stored in the stack?

  • Each thread has its own stack, and the data in the stack exists in the format of Stack Frame.
  • Each method being executed on this thread corresponds to a Stack Frame.
  • Stack frame is a memory block and a data set, which maintains various data information in the process of method execution.

Stack operation principle:

  • There are only two operations for JVM to directly stack Java stack, namely stack pressing and stack out of stack frame, following the principle of "first in first out" / "last in first out".
  • In an active thread, there is only 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, and the class defining this method is the Current Class.
  • All bytecode instructions run by the execution engine operate only on the current stack frame.
  • If other methods are invoked in the method, the corresponding new stack frame will be created and placed at the top of the stack, called the new current stack frame.

/**
 * @author shkstart
 * @create 2020 4:11 PM
 *
 * Methods end in two ways: ① end normally, represented by return; ② end in the way of throwing an exception when an uncapped exception occurs during method execution
 *
 */
public class StackFrameTest {
    public static void main(String[] args) {
        try {
            StackFrameTest test = new StackFrameTest();
            test.method1();

        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("main()Normal end");

    }

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

//        return ;// Can be omitted
}

    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;
    }

}

 

  • 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.
  • If the current method calls other methods, when the method returns, the current stack frame will return the execution result of this method to the previous stack frame, and then the virtual opportunity discards the current stack frame, so that the previous stack frame is renamed the current stack frame.
  • There are two ways for Java methods to return functions. One is normal function return, using the return instruction; The other is to throw an exception. No matter which method is used, the stack frame will be ejected.  


4.4 internal structure of stack frame

Each stack frame stores:

  • Local Variables
  • Operand Stack (or expression stack)
  • Dynamic Linking (or method reference that executes the runtime constant pool)
  • Method Return Address (or definition of normal or abnormal exit of the method)
  • Vaguely additional information

 
Multiple threads

4.4.1 local variable table


Example: verify that the size of the local variable table is determined by the compiler

import java.util.Date;

/**
 * @author shkstart
 * @create 2020 6:13 PM
 */
public class LocalVariablesTest {
    private int count = 0;

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

    //practice:
    public static void testStatic(){
        LocalVariablesTest test = new LocalVariablesTest();
        Date date = new Date();
        int count = 10;
        System.out.println(count);
        //Because this variable does not exist in the local variable table of the current method!!
//        System.out.println(this.count);
    }

    //Understanding of the use of Slot
    public LocalVariablesTest(){
        this.count = 1;
    }

    public void test1() {
        Date date = new Date();
        String name1 = "atguigu.com";
        String result = 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;
    }

    public void test3() {
        this.count++;
    }

    public void test4() {
        int a = 0;
        {
            int b = 0;
            b = a + 1;
        }
        //Variable c uses the position of the slot occupied by the previously destroyed variable b
        int c = a + 1;
    }

    /*
    Classification of variables: by data type: ① basic data type ② reference data type
              According to the position declared in the class: ① member variables: they have undergone default initialization and assignment before use
                                                Class variables: prepare phase of linking: default assignment to class variables -- > initial phase: explicit assignment to class variables, that is, assignment to static code blocks
                                                Instance variable: with the creation of the object, the instance variable space will be allocated in the heap space and assigned by default
                                  ② Local variable: it must be explicitly assigned before use! Otherwise, the compilation fails
     */
    public void test5Temp(){
        int num;
        //System.out.println(num);// Error message: variable num is not initialized
    }

}

You can see it by using the javap -v LocalVariablesTest.class command or the jclasslib tool (idea plug-in)

Using the jclasslib plug-in to view

  • 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 variable table, the expansion of the local variable table, and the larger its stack frame, so as to meet the demand of increasing the information required for method calls. Furthermore, function calls will occupy more stack space, resulting in fewer nested calls.
  • 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

  Demonstrates the bytecode of static functions     Generation completion

 


In the figure above, Start PC is the starting position of the variable scope, and length is the length of the scope

Understanding of Slot

  • Parameter values are always stored from index0 of the local variable array to the end of the index with the array length of - 1.
  • For the local variable table, the most basic storage unit is Slot (variable Slot)
  • The local variable table stores variables of various basic data types (8 types), reference types and returnAddress types known during compilation.
  • In the local variable table, types within 32 bits only occupy one slot (including returnAddress type), and 64 bit types (long and double) occupy two slots.
    • byte, short and char are converted to bit int before storage, and boolean is also converted to int. 0 means false, and non-0 means true
    • long and double occupy two slots.
  • 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
  • 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 in order
  • 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: access long or double variables)
  • If the current stack frame is created by the constructor or instance method, the object reference this will be stored in the slot with index 0, and the other parameters will continue to be arranged in the order of the parameter table

  Example:

  Example:

  Reuse of Slot

The slots in the local variable table in the stack frame can be reused. If a local variable exceeds its scope, the new local variable declared after its scope may reuse the slot of the expired local variable, so as to save resources.

  Example:


Example:


Example: comparison between static variables and local variables

  • After the parameter table is allocated, it is allocated according to the order and scope of variables defined in the method body.
  • We know that the class variable table is initialized twice. The first time is in the "preparation stage" to perform system initialization and set the zero value of the class variable. The other time is in the "initialization" stage to give the programmer the initial value defined in the code.
  • Unlike class 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.

Example: such code is wrong and cannot be used without assignment

  Classification of variables:

By data type: ① basic data type ② reference data type

According to the position declared in the class: ① member variables: they are initialized and assigned by default before use (class variables - prepare stage of linking: assign values to class variables by default, initial stage: assign values to class variables, that is, assign values to static code blocks; Instance variable - "with the creation of the object, the instance variable space will be allocated in the heap space and assigned by default); ② Local variable: the display value must be assigned before use, otherwise, the compilation will not pass.

Supplementary notes:

  • In the stack frame, the part most closely related to performance tuning is the local variable table mentioned earlier. When the method is executed, the virtual machine uses the local variable table to complete the transfer of the method.
  • Variables in the local variable table are also important garbage collection root nodes. Objects directly or indirectly referenced in the local variable table will not be recycled


4.4.2 Operand Stack
Stack: first in first out, last in first out, which can be realized by array and linked list

  • 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, write data or extract data to the stack according to bytecode instructions, that is, push / pop.
    • Some bytecode instructions push values into the operand stack, while others take operands out of the stack. Use them and then push the results onto the stack.
    • For example, perform assignment, exchange, sum and other operations


Example:

  • The operand stack is mainly used to save the intermediate results of the calculation process and serve as a temporary storage space for variables in 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. At this time, 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 stored in the Code attribute of the method, which is max_ The 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 use azimuth index to access data, but can only access data once through 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 elements in the operand stack must strictly match the sequence of bytecode instructions, 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 stack refers to the operand stack.

4.4.3 code tracking

bipush: put 15 on the operand stack, and the instruction address of the pc register is 0; Then, the instruction address of the pc register moves down to the instruction with instruction address 2, istore_1. i indicates int type_ 1 means to take out the stack top data and save it in the variable with index 1 in the local variable table;

 

  iload_1,iload_2 means to take out the values of variables with indexes 1 and 2 in the local variable table and put them on the stack

iadd is to take the data in the operand stack out of the stack in turn and perform the add operation; istore_3 means that the result of add is saved in the variable with index 3 in the local variable table as int type;

Example with return value

--------The difference between i + + and + + i often encountered in the interview process-------------

package com.atguigu.java1;

/**
 * @author shkstart
 * @create 2020 10:25 PM
 */
public class OperandStackTest {
    public void testAddOperation() {
        //byte, short, char, boolean: all are saved in int type
        byte i = 15;
        int j = 8;
        int k = i + j;

       // int m = 800;

    }

    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;
    }

    /*
    During the programmer interview, the common differences between i + + and + + i will be introduced in the bytecode chapter.

     */
    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;
    }
}

 

4.4.4 top of stack caching technology

As mentioned earlier, the lead address instructions used by the virtual machine based on the stack architecture are more compact, but it is necessary to use more input and output instructions when completing an operation, which also means that more instruction dispatch times and memory read and write times will be required.

Because the operand stack is stored in memory, frequent memory read and write operations will inevitably affect the execution speed. In order to solve this problem, the designers of the HotSpot JVM proposed the top of stack caching (ToS) technology, which caches all the top of stack elements in the registers of the physical cpu, so as to reduce the number of reads and writes to the memory and improve the execution efficiency of the execution engine.

4.4.5 Dynamic Linking

Dynamic links (or method references to runtime constant pools)

  • Each stack frame contains a reference to the method to which the stack frame belongs in the execution runtime constant pool. The purpose of including this reference is to support the code of the current method to realize Dynamic Linking. For example, the invokedynamic instruction
  • 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. 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.

package com.atguigu.java1;

/**
 * @author shkstart
 * @create 2020 10:25 PM
 */
public class DynamicLinkingTest {

    int num = 10;

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

    public void methodB(){
        System.out.println("methodB()....");

        methodA();

        num++;
    }

}

javap -v DynamicLinkingTest.class


Look at the bytecode instructions and dynamic links of the methodB method

 

#The beginning is a symbol reference. What the arrow does not specify is a direct reference

Why do I need a constant pool?

The function of constant pool is to provide some conformances and constants to facilitate the identification of instructions.  


4.4.6 method call

In the JVM, converting a symbolic reference to a direct reference calling a method is related to the binding mechanism of the method.

  • Static link:
    • When a bytecode file is loaded inside the JVM, if the called target method is known at compile time and remains unchanged at run time. In this case, the process of converting the symbolic reference of the calling method into a direct reference is called static linking
  • Dynamic link:
    • If the called method cannot be determined at the compile time, that is, the symbolic reference of the calling method can only be converted into a direct reference at the program run time. Because the reference conversion process is dynamic, it is also called dynamic link.

The binding mechanisms of the corresponding methods are Early Binding and Late Binding. Binding is a process in which a field, method, or class is directly referenced when a symbolic reference is replaced, which occurs only once.

  • Early binding:
    • Early binding means that if the called target method is known at the compile time and remains unchanged at the run time, this method can be bound with its type. 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.
  • 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.

Example:

package com.atguigu.java2;

/**
 * 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
    }
}

  Polymorphic calls are late references
Multiple constructors call the constructor of the parent class or other constructor methods, that is, the method specified by the method call belongs to early binding

With the emergence of high-level languages, there are more and more object-oriented programming languages like 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 used to display the definition). If you do not want a method to have the characteristics of a virtual function in a java program, you can mark the method with the keyword final.

Virtual functions are methods that can only be determined at run time

Virtual and non virtual methods:

Non virtual method:

  • If the method determines the specific calling 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, instance constructors, and parent methods are all non virtual methods.
  • Other methods are called virtual methods.

The following method call instructions are provided in the virtual machine:

  • Normal call instruction:
  1. invokestactic: call static methods, and determine the unique method version in the parsing phase
  2. invokespecial: call < init > methods, private and parent methods, and determine the unique method version in the parsing phase
  3. invokevirtual: calls all virtual methods
  4. invokeinterface: invokes interface methods
  • Dynamic call instruction:
  1. invokedynamic: dynamically resolve the method to be called, and then execute

The first four instructions are solidified in the virtual machine, and the calling and execution of methods cannot be interfered by human beings, while the invokedynamic instruction supports the user to determine the method version. 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.

Example:

package com.atguigu.java2;

/**
 * Testing of non virtual methods and virtual methods in parsing calls
 * <p>
 * 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!");
        //invokespecial
        super.showCommon();

        //invokevirtual
        showFinal();//Because this method has a final declaration and cannot be overridden by subclasses, it is also considered a non virtual method.
        //The virtual method is as follows:
        //invokevirtual
        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();
}

About invokedynamic directive

  • The JVM bytecode instruction set has been relatively stable until Java 7 added an invokedynamic instruction, which is an improvement made by java to realize the support of "dynamically typed language".
  • However, Java 7 does not provide a method to directly generate invokedynamic instructions. It is necessary to generate invokedynamic instructions with the help of ASM, a low-level bytecode tool. Until the emergence of Java 8 Lamdba expression and the generation of invokedynamic instructions, there was no 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.

Dynamically typed languages and statically typed languages

The difference between dynamic type language and static type language lies in whether the type is checked at compile time or run time. The former is static type language, and the other is dynamic type language.

To put it bluntly, static type 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.

Example:

package com.atguigu.java2;

/**
 * Experience invokedynamic instruction
 * @author shkstart
 * @create 2020 3:09 PM
 */
@FunctionalInterface
interface Func {
    public boolean func(String str);
}

public class Lambda {
    public void lambda(Func func) {
        return;
    }

    public static void main(String[] args) {
        Lambda lambda = new Lambda();

        Func func = s -> {
            return true;
        };

        lambda.lambda(func);

        lambda.lambda(s -> {
            return true;
        });
    }
}

 

The essence of method rewriting in Java language:

  1. Find the actual type of the object executed by the first element at the top of the operand stack and record it as C.
  2. If you find a method in type C that matches both the familiar and simple names in the constant, you will perform access permission verification. If it passes, you will return the direct reference of this method, and the search process ends; If it fails, a java.lang.IllegalAccessError exception is returned.
  3. Otherwise, search and verify each parent class of C from bottom to top according to the inheritance relationship in step 2.
  4. If no suitable method is found, a java.lang.AbstractMethodError exception is thrown.

Introduction to IllegalAccessError:

The program attempts to access or modify a property or call 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 occurred to a class.

Published by the demander

  • Dynamic dispatch is frequently used in object-oriented programming. If you have to search the appropriate target in the method metadata of the class again in the process of 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.
  • Each class has a virtual method table, which stores the actual entries of each method.
  • So when is the virtual method table created? The virtual method table will be created and initialized in the link phase of class loading. After the initial value of the class variable is prepared, the JVM will initialize the Party of the class.

 

package com.atguigu.java3;

/**
 * Examples of virtual method tables
 *
 * @author shkstart
 * @create 2020 1:11 PM
 */
interface Friendly {
    void sayHello();
    void sayGoodbye();
}
class Dog {
    public void sayHello() {
    }
    public String toString() {
        return "Dog";
    }
}
class Cat implements Friendly {
    public void eat() {
    }
    public void sayHello() {
    }
    public void sayGoodbye() {
    }
    protected void finalize() {
    }
    public String toString(){
        return "Cat";
    }
}

class CockerSpaniel extends Dog implements Friendly {
    public void sayHello() {
        super.sayHello();
    }
    public void sayGoodbye() {
    }
}

public class VirtualMethodTable {
}

 

Method return address

  • Store the value of the pc register calling the method.
  • There are two ways to end a method:
    • Normal execution completed
    • Unhandled exception occurred, abnormal exit
  • No matter which way you exit, you will return to the location where the method is called after the method exits. When the method exits normally, the value of the caller's pc counter is used as the return address and the specified address of the next item calling the method. 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.

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

  1. When the execution engine encounters the bytecode instruction (return) returned by any method, the return value will be passed to the upper method caller, which is referred to as the normal completion exit.
    1. Which return instruction a method needs to use after the normal call is completed depends on the actual data type of the method return value.
    2. In bytecode instructions, the return instructions include ireturn (used when the return values are boolean, byte,, char, short and int types), lreturn, freeturn, dreturn and areturn. In addition, the return instructions are used for the initialization methods of methods declared as void, instance initialization methods, classes and interfaces.
  2. An Exception is encountered during the execution of the method, and this Exception is not handled inside the method. That is, as long as no matching Exception handler is found in the Exception table of the stupid method, the method will exit, which is referred to as Exception completion exit for short.

The exception handling when an exception is thrown during the execution of the method is stored in an exception handling table to facilitate finding the code to handle the exception when an exception occurs.

Example:

In essence, 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.

The difference between normal completion exit and exception completion exit is that the exit through exception completion exit will not produce any return value to its upper caller.

Example:

package com.atguigu.java3;

import java.io.FileReader;
import java.io.IOException;
import java.util.Date;

/**
 *
 * The return instruction contains ireturn (used when the return values are boolean, byte, char, short and int types)
 * lreturn,freturn,dreturn And areturn. In addition, there is a return instruction for methods declared as void
 * Instance initialization methods, classes, and interface initialization methods are used.
 *
 * @author shkstart
 * @create 2020 4:05 PM
 */
public class ReturnAddressTest {
    public boolean methodBoolean() {
        return false;
    }

    public byte methodByte() {
        return 0;
    }

    public short methodShort() {
        return 0;
    }

    public char methodChar() {
        return 'a';
    }

    public int methodInt() {
        return 0;
    }

    public long methodLong() {
        return 0L;
    }

    public float methodFloat() {
        return 0.0f;
    }

    public double methodDouble() {
        return 0.0;
    }

    public String methodString() {
        return null;
    }

    public Date methodDate() {
        return null;
    }

    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();
    }


}

 

4.4.8 some additional information

The stack frame also allows some additional information related to the Java virtual machine implementation to be carried. For example, information that provides support for program debugging.

4.4.9 relevant interview questions

  •   For example, stack overflow?
  • Can adjusting the stack size ensure no overflow?
  • Is the larger the stack memory allocated, the better?
  • Will garbage collection be designed into the virtual machine stack?
  • Is the local variable defined in the method thread safe?

Example:

package com.atguigu.java3;

/**
 * Interview questions:
 * Is the local variable defined in the method thread safe? Specific situation and specific analysis
 *
 *   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.
 * @author shkstart
 * @create 2020 7:48 PM
 */
public class StringBuilderTest {

    int num = 10;

    //s1 is declared thread safe
    public static void method1(){
        //StringBuilder: thread unsafe
        StringBuilder s1 = new StringBuilder();
        s1.append("a");
        s1.append("b");
        //...
    }
    //Operation process of sBuilder: it is thread unsafe
    public static void method2(StringBuilder sBuilder){
        sBuilder.append("a");
        sBuilder.append("b");
        //...
    }
    //s1 operation: thread unsafe
    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);

    }

}

4.5 local method interface

Is it a local method?

In short, a Native Method is an interface for Jva to call non java code. A Native Method is a Java method implemented in a non Java language, such as C. This feature is unique to concurrent Java. Many other programming languages have this mechanism. For example, in C + +, you can use extern "C" to tell the C + + compiler to call a C function.

"A native ,ethdo is a Java method whose implementation is ptovided by non-java code."

When defining a native method, it does not provide an implementation (some like defining a Java interface), because its implementation is implemented outside in a non Java language.

The function of local interface is to integrate different programming languages for Java. Its original intention is to integrate C/C + + programs.

  Why use Native Method?

Java is easy to use, and then some level tasks are not easy to implement in Java, or we are very concerned about the efficiency of the program.

  • Interaction with Java environment:
    • Sometimes Java applications need to interact with environments other than Java, which is the main reason for the existence of local methods. You can imagine that Java needs to exchange information with some underlying systems, such as the operating system or some hardware. The local method is such a communication mechanism: it provides us with a very simple interface, and we don't need to understand the cumbersome details outside the Java application.
  • Interaction with operating system:
    • JVM supports the Java language itself and runtime library. It is the platform for Java programs to survive. It is composed of an interpreter (Interpreting bytecode) and some libraries connected to local code. However, it is not a complete system after all. It often depends on the support of some underlying systems. These underlying systems are often powerful operating systems. By using local methods, we can realize the interaction between jre and the underlying system in Java, and even some parts of the JVM are written in C. Also, if we want to use some features of the operating system that the Java language itself does not provide encapsulation, we also need to use local methods.
  • Sun's Java:
    • Sun's interpreter is implemented in C, which enables it to interact with the outside world like some ordinary C. jre is mostly implemented in Java, and it also interacts with the outside world through some local methods. For example, the setPriority () method of class java.lang.Thread is implemented in Java, but it calls the local method setPriority () in the class. This local method is implemented in C and embedded in the JVM. On the Windows 95 platform, this local method will eventually call the Win32 SetPriority() API. This is the specific implementation of a local method, which is directly provided by the JVM. More often, the local method is provided by an external dynamic link library and then called by the JVM.

present situation:     At present, this method is used less and less, except for hardware related applications, such as driving printers through Java programs or managing production devices through Java systems, which are rare in enterprise applications. Because now the communication between heterogeneous domains is very developed, such as Socket communication, Web Service and so on. I won't introduce more.

  4.6 Native Method Stack

  • The Java virtual machine stack is used to manage the calls of Java methods, while the local method stack is used to manage the calls of local methods.
  • The local method stack is also thread private.
  • It is allowed to be implemented as a fixed or dynamically scalable memory size( (same in terms of memory overflow)
    • If the thread requests that the allocated stack capacity exceeds the maximum allowed by the local method stack, the Java virtual machine will throw a StackOverflowError exception.
    • If the local method stack can be expanded dynamically, and cannot apply for enough memory when trying to expand, or there is not enough memory area to create the corresponding local method stack when creating a new thread, the Java virtual machine will throw an OutOfMemoryError exception.
  • Local methods are implemented in C language.
  • Its specific method is to register the native method in the Native Method Stack and load the local method library when the Execution Engine executes.

 

  •   When a thread calls a local method, it enters a new world that is no longer limited by virtual machines. It has the same permissions as the virtual machine.
    • Local methods can access the runtime data area inside the virtual machine through the local method interface.
    • It can even use the registers in the local processor directly.
    • Allocate any amount of memory directly from the heap of local memory.
  • Not all JVMs support local methods. Because the Java virtual machine specification does not explicitly require the use language, specific implementation method, data structure, etc. of the local method stack. If the JVM product is not intended to support native methods, there is no need to implement a local method stack.
  • In the Hotspot JVM, the local method stack and virtual machine stack are combined directly.

  

Posted by stitchmedia on Tue, 07 Sep 2021 11:09:21 -0700