1. Vm bytecode execution engine

1.1 Runtime stack frame structure

We already know that there is a region, and we already know that there is a stack frame. So let’s analyze stack frames in terms of method execution.

1.1.1 overview

A stack frame, also known as a procedure activity record, is a data structure used by the compiler for method invocation and method execution. It is the stack element of the virtual machine stack in the data area when the virtual machine is running. Stack frame included local variables, the operand stack, and dynamically linked methods return address and additional some additional information, in the process of compilation, the size of the local variables have determined that the operand stack depth has been determined, so the stack frame in the process of running need to assign how much memory is fixed, not influenced by the runtime. For objects that do not escape, memory is allocated on the stack, and the size of the object is actually determined at run time, so even if an on-stack allocation occurs, it does not cause the stack frame to change size.

In a thread, the call chain may be very long and many methods may be executing at the same time. For the execution engine, only the stack frame at the top of the stack is most effective in the active thread, called the current stack frame, and the method associated with this stack frame is called the current method. The bytecode instructions run by the execution engine operate only on the current stack frame.

  • Local variable scale
  • The operand stack
  • Dynamic connection
  • Method return address
  • Additional information

1.1.2 Method Invocation

Parse dispatch dynamic language support

2. Local variation scale

2.1 an overview of the

  • Local Variable Table is a set of Variable value storage space used to store method parameters and Local Variable Table defined within a method. When Java is compiled as a class file, the max_locals data item in the Code property of the method defines the maximum amount of local variables that the method needs to allocate.
  • The capacity of the local Variable table is the smallest unit in Variable Slot.

2.2 Local variables

  • Global variables are not initialized, no problem
  • Local variables that do not have an initial value are reported by compilation
Public class VDemo {private int a; public class VDemo {private int a; private int b; public int add() { return a + b; } public int add2() { int a; int b; // return a+b; Return 0; }}Copy the code

2.3 Slot.

Byte Boolean short char int float double long reference: specifies the fixed size. One Slot is 32 bytes. If the Slot is not enough (for example, double), two slots (64 bytes) are used ReturnAdress does not have thread-safety issues because it is thread-exclusive.

Slot reuse A Slot can be reused when the value of a variable’s PC register is greater than the scope of the Slot

public class GCDemo { public static void main(String[] args) { byte [] buff = new byte[60 * 1024 * 1024]; System.gc(); * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * [GC (system.gc ()) 66601K->62451K(247296K), 0.0229185 secs] [Full GC (system.gc ()) 62451K->62335K, 0.0131974 secs] public class GCDemo2 {public static void main(String[] args) {{byte[] buff = new byte[60 * 1024 * 1024]; } // Reclaim out of buff's scope system.gc (); // Again, memory is not reclaimed because slot is not used at any other point and will be reclaimed if used. GCDemo3}} -verbose:gc: GCDemo3 [GC (system.gc ()) 66601K->62451K, 0.0014292secs] [Full GC (system.gc ()) 62451K->62335K, 0.0063652 secs] public class GCDemo3 {public static void main(String[] args) {{byte[] buff = new byte[60 * 1024 * 1024]; } int a = 10; // Read to slot. System.gc(); * * * * * * * * * * * * * * * * * * * * * * * * * [GC (system.gc ()) 66601K->62419K(247296K), 0.0141469 secs] [Full GC (system.gc ()) 62419K->895K, GCDemo3 Full GC 62419K->895K;Copy the code
  • Buffs will not be recycled immediately after they are out of scope, but will be recycled only after new use. Just like whether the data of the hard disk will be overwritten, the deletion is only marked deletion, and the data will be overwritten again.
  • 2.4 Thread Unsafe Scenario

Multithreading requires non-atomic operations on shared resources

Operand stack

3.1 an overview of the

The interpreted execution engine of the Java virtual machine is called “stack-based execution engine”, where the stack refers to the -operand stack.

Operand stacks are also often referred to as operation stacks. Like the local variable area, the operand stack is organized into an array of word lengths. Unlike the former, however, it is accessed not through indexes, but through standard stack operations-pushing and unpushing. For example, if an instruction pushes a value onto the operand stack, another instruction can pop that value up for use later.

The virtual machine stores data in the operand stack the same way it stores data in the local variable area: storage of ints, longs, floats, doubles, and references. Byte, short, and CHAR values are also converted to ints before being pushed onto the operand stack.

For example, 3.2

public class StackNumDemo { public int add(int a,int b){ return a+b; }}Copy the code

The virtual machine uses the operand stack as its workspace, where most instructions eject data, perform operations, and then push the results back into the operand stack. For example, iadd pops two integers from the operand stack, performs an addition operation, and pushes the result back into the operand stack. Look at the following example, which shows how the virtual machine adds two local variables of type INT and saves the result to a third local variable:

    iload_0    // push the int in local variable 0 onto the stack  
    iload_1    // push the int in local variable 1 onto the stack  
    iadd       // pop two ints, add them, push result  
    istore_2   // pop int, store into local variable 2  
Copy the code

In this bytecode sequence, the first two instructions, ILoAD_0 and ILoAD_1, push the integers indexed 0 and 1 stored in local variables into the operand stack, and the iADD instruction adds the two integers from the operand stack and pushes the result into the operand stack. The fourth instruction, istore_2, pops the result from the operand stack and stores it at index 2 in the local variable area. The following figure details the state changes of local variables and operand stacks during this process, with unused areas of local variables and operand stacks represented by blank Spaces.

  • Although stack to stack is thread independent, there will be data transfer between stacks, so the virtual machine will have stack communication online.

4 Dynamic Connection

  • Dynamic connection

Each stack frame contains a reference to the method that the stack belongs to in the runtime constant pool. This reference is held to support Dynamic Linking during the method invocation. The Class file contains a large number of symbols in the constant pool, and the method invocation instructions in the bytecode take symbolic references to methods in the constant pool as arguments.

    • Part of this symbolic reference is converted to a direct reference during class loading or the first time it is used, which becomes static resolution.
    • The other part is converted to a direct reference during each run, and this part becomes the dynamic join.

See :7 and 8 for details

The 5 method returns the address and additional information

5.1 Method Return address

  • Normal exit completion

When the execution engine encounters a bytecode instruction returned by any method (return), it passes the return value to the upper-level method caller. Whether there is a return value and the type of return value depends on what method return instruction is encountered. When a method is called, it points to the address of the method through a pointer to the method, and when the method returns, it returns to the call, which is the return address.

  • Exception completion exit code in the use of athrow bytecode, the exception handler table to handle the exception return address.

5.2 Additional Information

The virtual machine specification allows specific virtual machine implementations to add information to the stack frame that is not described in the specification. This information depends entirely on the implementation of the virtual machine.

6 Method Invocation

6.1 Parsing Calls

6.1.1 overview

  • parsing

All method calls the target method inside the Class files are in a constant pool in symbolic reference, in the phase of Class loading, will be one part of a symbolic reference into direct reference, this resolution will set up the premise is: one method before the program is really running can determine call version, version and this method is called at runtime is not To change. In other words, the call target must be determined when the program code is written and the compiler compiles. Calls to such methods are called parsing. That is, method invocation is not the same as method execution. The only task of the method invocation phase is to determine the version of the method being invoked.

  • Methods that meet the requirements of “compile time knows, run time does not change” mainly include static methods and private methods.

The former are directly related to the type, while the latter are not externally accessible, and their respective properties make them impossible to recreate other versions by inheritance or otherwise, since they are suitable for parsing at class load time.

  • The bytecode instructions for method calls are:
    • Invokestatic invokes static methods
    • Invokespecial invokes instance constructor methods, private methods, and superclass methods.
    • Invokevirtual calls all virtual methods.
    • Invokeinterface invokes the interface method, which determines at run time an object that implements the interface
    • The InvokeDynamic runtime now dynamically resolves the method referenced by the call point qualifier and then executes that method.

The first four instructions are hardwired into the Java virtual machine, whereas the InvokeDynamic is determined by the bootstrapped method specified by the user.

  • Any method that can be invoked by Invokestatic and Invokespecial directives can determine the unique invocation version during the parsing phase. Those eligible are:

    • A static method
    • Private methods
    • Constructor method
    • Method of final decoration
  • Java features: encapsulation, inheritance, polymorphism

6.1.2 interpretation

public class MethodStatic0 { public static void sayHello(){ System.out.println("Hello"); } public static void main(String[] args) { MethodStatic0.sayHello(); } } public static void sayHello(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=0, args_size=0 0: getstatic #2 // Field java/lang/System.out:Ljav a/io/PrintStream; 3: ldc #3 // String Hello 5: invokevirtual #4 // Method java/io/PrintStream.prin tln:(Ljava/lang/String;) V 8: return LineNumberTable: line 5: 0 line 6: 8 public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=0, locals=1, args_size=1 0: invokestatic #5 // Method sayHello:()V 3: return LineNumberTable: line 9: 0 line 10: 3 LocalVariableTable: Start Length Slot Name Signature 0 4 0 args [Ljava/lang/String; } SourceFile: SayHello () : 0: sayHello () : 0: sayHello () : 0: sayHello () : 0: sayHello () : 0: invokestatic #5 // Method sayHello:()VCopy the code

6.2 Method Calls – Statically dispatch calls

6.2.1 overview

  • Static dispatch calls focus on method overloading
  • Static dispatch calls focus on method rewriting

Static dispatch is determined at compile time.

  • Any method that can be invoked by Invokestatic and Invokespecial directives can determine the unique invocation version during the parsing phase. Those eligible are:
    • A static method
    • Private methods
    • Constructor method
    • Method of final decoration

6.2.2 Method Static Dispatch Example (- Static Class)

public class MethodStatic1 { static class Parent{ } static class Child1 extends Parent{} static class Child2 extends Parent{} public void sayHello(Child1 c){ System.out.println("Child1 is call"); } public void sayHello(Child2 c){ System.out.println("Child2 is call"); } public void sayHello(Parent c){ System.out.println("Parent is call"); } public static void main(String[] args) { Parent p1 = new Child1(); Parent p2 = new Child2(); MethodStatic1 m = new MethodStatic1(); m.sayHello(p1); //Parent is call m.sayHello(p2); //Parent is call Parent p = new Child1(); p = new Child2(); // The actual type changes, but so does the static method type. (Child2)p); //Parent is called // static type is changed. Parent is call Parent is call Parent is call Parent is call Child2 is call // Static method types are determined at compile time, so they reflect compile-time types.Copy the code
Javap -verbose The preceding file classes are as follows: 26: invokevirtual #13 // Method sayHello:(Lcourse/jvmstu /bytecodeexecutionengine/MethodStatic1$Parent;) V 29: aload_3 30: aload_2 31: invokevirtual #13 // Method sayHello:(Lcourse/jvmstu /bytecodeexecutionengine/MethodStatic1$Parent;) V 34: new #7 // class course/jvmstu/bytecodeexe cutionengine/MethodStatic1$Child1 37: dup 38: invokespecial #8 // Method course/jvmstu/bytecodeex ecutionengine/MethodStatic1$Child1."<init>":()V 41: astore 4 43: new #9 // class course/jvmstu/bytecodeexe cutionengine/MethodStatic1$Child2 46: dup 47: invokespecial #10 // Method course/jvmstu/bytecodeex ecutionengine/MethodStatic1$Child2."<init>":()V 50: astore 4 52: aload_3 53: aload 4 55: invokevirtual #13 // Method sayHello:(Lcourse/jvmstu /bytecodeexecutionengine/MethodStatic1$Parent;) V 58: aload_3 59: aload 4 61: checkcast #9 // class course/jvmstu/bytecodeexe cutionengine/MethodStatic1$Child2 64: invokevirtual #14 // Method sayHello:(Lcourse/jvmstu /bytecodeexecutionengine/MethodStatic1$Child2;) V 67: returnCopy the code

Looking at the bytecode above, you can see that the type of the static method is determined at compile time and therefore reflects the type at compile time.

  • Static types are also called Apparent types: the Parent in this example is a Static Type. Static types are known at compile time.

  • Actual types, Child1 and Child2 in the previous example are Actual types. The actual type is known at run time. You don’t know what the actual type is at compile time.

6.2.3 Example of priority matching for overloaded methods

  • Static dispatch occurs at compile time and is not determined by the virtual machine. The compiler can determine the overloaded version of a method, but in many cases this overloaded version is not “unique” and only a “more suitable” version can be determined. The following example is an example.
public class MethodStatic2 { public void sayHello(short a) { System.out.println("short"); } public void sayHello(int a) { System.out.println("int"); } public void sayHello(long c) { System.out.println("long"); } public void sayHello(char c) { System.out.println("char"); } public void sayHello(Character c) { System.out.println("Character"); } public void sayHello(Object c) { System.out.println("Object"); } public void sayHello(char... c) { System.out.println("char..." ); } public void sayHello(Serializable c) { System.out.println("Serializable..." ); } public static void main(String[] args) { new MethodStatic2().sayHello('c'); // Char is first used, then commented out, int is called (c also stands for unicode decimal 97), long is called when int is omitted, and so on. Char >>int> Long >>Charter>Serializable>Object>> Char... }}Copy the code

7 Method call – Dynamically dispatch the call

7.1 an overview of the

  • Static dispatch calls focus on method overloading
  • Static dispatch calls focus on method rewriting

The process of invokevirtual:

  • 1. Find the actual type of the object to which the first element at the top of the operand stack points
  • 2.1 If a method is found in the actual type that matches the descriptor and simple name in the constant, the access permission is verified. 2.1.1 If the method passes, the direct reference of the method is returned, and the search process ends. 2.1.2 If no, an exception is thrown. 2.2 If no method is found, proceed to 3.
  • 3. Search and verify the parent classes of the actual type from bottom to top according to the inheritance relationship
  • 4. If it is never found, AbstractMethodError is raised

For example, 7.2

public class MethodDynamic { static class Parent{ public void sayHello(){ System.out.println("come to Parent"); } } static class Child1 extends Parent{ @Override public void sayHello() { System.out.println("come to Child1"); } } static class Child11 extends Child1{ @Override public void sayHello() { System.out.println("come to Child11"); } } static class Child2 extends Parent{ @Override public void sayHello() { System.out.println("come to Child2"); } } public static void main(String[] args) { Parent p1 = new Child1(); Parent p2 = new Child2(); p1.sayHello(); //come to Child1 p2.sayHello(); //come to Child2 Parent p11 = new Child11(); p11.sayHello(); //come to Child11}} // Result come to Child1 come to Child2 come to Child11Copy the code

The javap-verbose command output is as follows:

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         0: new           #2                  // class course/jvmstu/bytecodeexe
cutionengine/MethodDynamic$Child1
         3: dup
         4: invokespecial #3                  // Method course/jvmstu/bytecodeex
ecutionengine/MethodDynamic$Child1."<init>":()V
         7: astore_1
         8: new           #4                  // class course/jvmstu/bytecodeexe
cutionengine/MethodDynamic$Child2
        11: dup
        12: invokespecial #5                  // Method course/jvmstu/bytecodeex
ecutionengine/MethodDynamic$Child2."<init>":()V
        15: astore_2
        16: aload_1
        17: invokevirtual #6                  // Method course/jvmstu/bytecodeex
ecutionengine/MethodDynamic$Parent.sayHello:()V
        20: aload_2
        21: invokevirtual #6                  // Method course/jvmstu/bytecodeex
ecutionengine/MethodDynamic$Parent.sayHello:()V
        24: new           #7                  // class course/jvmstu/bytecodeexe
cutionengine/MethodDynamic$Child11
        27: dup
        28: invokespecial #8                  // Method course/jvmstu/bytecodeex
ecutionengine/MethodDynamic$Child11."<init>":()V
        31: astore_3
        32: aload_3
        33: invokevirtual #6                  // Method course/jvmstu/bytecodeex
ecutionengine/MethodDynamic$Parent.sayHello:()V
        36: return
      LineNumberTable:
        line 30: 0
        line 31: 8
        line 33: 16
        line 34: 20
        line 37: 24
        line 38: 32
        line 40: 36
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      37     0  args   [Ljava/lang/String;
            8      29     1    p1   Lcourse/jvmstu/bytecodeexecutionengine/Metho
dDynamic$Parent;
           16      21     2    p2   Lcourse/jvmstu/bytecodeexecutionengine/Metho
dDynamic$Parent;
           32       5     3   p11   Lcourse/jvmstu/bytecodeexecutionengine/Metho
dDynamic$Parent;
}
SourceFile: "MethodDynamic.java"
Copy the code

Dynamically typed language support

8.1 an overview of the

  • In statically typed languages, the type of a variable is determinable, that is, it is typed (int I = 0;).

  • In dynamically typed languages, the type of a variable is indeterminate at non-runtime, that is, the variable is untyped, but the value is typed, that is, the value of the variable can be determined at runtime (var a = 20;).

  • Dynamic languages :javascript, Scale, Groovy

For example, 8.2

Java executes javascript scripts

import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; public class ClassDynamic { public static void main(String[] args) throws ScriptException { ScriptEngineManager sem = new ScriptEngineManager(); ScriptEngine se= sem.getEngineByName("JavaScript"); Object obj = se.eval("function add(a,b) {return a+b} add(2,3)"); System.out.println(obj); //5.0}} // Result 5.0Copy the code