Deep understanding of jvm-stack frame & method call

This article is a reading note

1. Basic concepts

Insert picture description here
The Java virtual machine uses methods as the most basic execution unit . "Stack Frame" is a data structure used to support the method invocation and method execution of the virtual machine. Stack element of the Virtual Machine Stack.

** Basic composition: ** Local variable table, operand stack, dynamic link, method return address and some additional additional information.

When compiling the source code of the Java program , how much local variable table is needed in the stack frame, how deep the operand stack needs to be analyzed and calculated, and written into the Code property of the method table

From the perspective of a Java program, all methods on the call stack are in the same state at the same time and in the same thread. For the execution engine, in the active thread, only the method at the top of the stack is running, and only the stack frame at the top of the stack is effective , which is called ** "current stack frame" ** ( CurrentStack Frame), the method associated with this stack frame is called "Current Method".

There is a part in the springboot source code:

This part is in the construction method of SpringApplication.class, the class that gets the entry of the main method, its operation is to get the current stack frame; debug can clearly see that the
Insert picture description here
basic process of the current stack frame is to create a runtime exception, and then get the stack array, traverse StackTraceElement array, judge whether the method name is "mian", if yes, create Class object by Class.forName ()

The code is very simple, but does the use of SpringBoot make us feel very enlightening. Let's compare Java's exception handling and learn more about the use of StackTrace.

Stacktrace (stack trace) is a very useful debugging tool. When an exception occurs in the program or manually throws an exception, the error place can be displayed, causing the hierarchical relationship of the error.

2. Local Variable Table

Storage space for storing method parameters and local variables defined inside the method.
When the Java program is compiled into a Class file, the maximum capacity of the local variable table that the method needs to allocate is determined in the max_locals data item of the Code attribute of the method.

For 32-bit boolean, byte, char, short, int, float, reference [illustration] and returnAddress, one slot is enough.
For 64-bit data types, the Java virtual machine allocates two consecutive variable slot spaces in a high-bit aligned manner.

The Java virtual machine uses the local variable table through index positioning . ** The index value range is from 0 to the maximum number of variable slots in the local variable table. ** If you are accessing a variable of 32-bit data type, index N represents the use of the Nth variable slot. If you are accessing a variable of 64-bit data type, it means that both Nth and N + 1 variables will be used groove.

When a method is called, the Java virtual machine uses the local variable table to complete the transfer process of the parameter value to the parameter variable list, that is, the transfer of the actual parameter to the formal parameter.

The variable slot in the local variable table is reusable.
Example of reuse:
Insert picture description here
if there is no int a = 0; gc will not be triggered, and gc will be triggered after it is available;
reason:
whether the variable slot in the local variable table still has some information Placeholder array object reference. In the first modification, although the code has left the scope of the placeholder, but after this, no read or write operations to the local variable table have occurred, the variable slot originally occupied by the placeholder has not been reused by other variables , So the local variable table as part of GC Roots still maintains its association.

3. Operand stack

The operand stack (Operand Stack) is often called the operation stack, which is a last-in-first-out (Last In First Out, LIFO) stack.

Each element of the operand stack can be any Java data type including long and double. The stack capacity occupied by the 32-bit data type is 1, and the stack capacity occupied by the 64-bit data type is 2.

4. Dynamic connection

Each stack frame contains a reference to the method to which the stack frame belongs in the runtime constant pool [illustration]. This reference is held to support dynamic connection during method invocation.

5. Method return address

There are only two ways to exit the method: 1. return normally and return the value to the upper caller 2. throw an exception and the exception is not properly handled.
Generally speaking, when the method exits normally, the value of the PC counter of the calling method can be used as the return Address, stack counter is likely to save this counter value. When the method exits abnormally, the return address is determined by the exception handler table, and this part of information is generally not saved in the stack frame.
The possible operations when exiting are: restore the local variable table and operand stack of the upper method, push the return value (if any) into the operand stack of the caller's stack frame, adjust the value of the PC counter to point to the method call An instruction after the instruction, etc.

6. Additional information

Information related to debugging and performance collection. This part of the information depends entirely on the specific virtual machine implementation.
Generally, dynamic connections, method return addresses, and other additional information are all grouped together, called stack frame information.

Example: i ++, ++ i

Also called stack-based interpretation execution

int a = 10;
 int b = a++ + ++a + a--; 
 System.out.println(a); 
 System.out.println(b);

Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
Insert picture description here
The picture comes from the dark horse decryption of the station b jvm

7. Method call

The compilation process of the Class file does not include the connection steps of the traditional programming language compilation. All method calls stored in the Class file are only symbolic references, not the entry address of the method in the actual runtime memory layout (that is, the direct Reference).

The Java virtual machine supports the following five methods to call bytecode instructions, namely: invokestatic. Used to call static methods.

· Invokespecial. Used to call the instance constructor <init> () method, private methods and methods in the parent class.

· Invokevirtual. Used to call all virtual methods.

· Invokeinterface. Used to call interface methods, and an object that implements the interface will be determined at runtime.

· Invokedynamic. The method referenced by the call point qualifier is dynamically resolved at runtime, and then the method is executed. The dispatch logic of the first 4 call instructions is fixed in the Java virtual machine, and the dispatch logic of the invokedynamic instruction is determined by the boot method set by the user.

As long as the methods that can be invoked by the invokestatic and invokespecial instructions, the only calling version can be determined during the parsing phase. There are four methods in the Java language that meet this condition: static methods, private methods, instance constructors, and superclass methods, plus The method modified by final (although it is invoked using invokevirtual instruction) , these five method calls will resolve symbol references into direct references to the method when the class is loaded . These methods are collectively referred to as "Non-Virtual Methods". In contrast, other methods are referred to as "Virtual Methods".

Dispatch

Man man = new Women ();
Man is static type, Women is dynamic type;

So the execution of a method is divided into static dispatch and dynamic dispatch according to the static type on the left or the dynamic type on the right;
** The most typical application performance of static dispatch is method overloading. ** Static dispatching occurs during the compilation phase, so it is determined that the static dispatching action is not actually performed by the virtual machine, which is why some materials choose to classify it as "parse" rather than "dispatch".

The implementation process of dynamic dispatch is closely related to another important manifestation of Java language polymorphism-Override.

Dynamically typed language

The key feature of a dynamically typed language is that the main process of its type checking is
"variables have no types but variable values ​​have types" at runtime rather than at compile time.

The following is a dynamic method inside java

The java.lang.invoke package executes println () in A

Insert picture description here

It looks like reflection, but
MethodHandle has many similarities with Reflection in the use of methods and effects:

The Reflection and MethodHandle mechanisms are essentially simulating method invocation, but ** Reflection is simulating method invocation at the Java code level, and MethodHandle is simulating method invocation at the bytecode level. ** The three methods findStatic (), findVirtual (), findSpecial () on MethodHandles.Lookup are to correspond to the execution permission verification behavior of these bytecode instructions invokestatic, invokevirtual (and invokeinterface) and invokespecial These low-level details do not need to be concerned when using the Reflection API.

** The java.lang.reflect.Method object in Reflection is much more than the information contained in the java.lang.invoke.MethodHandle object in the MethodHandle mechanism. ** The former is a comprehensive image of the method on the Java side, including the method's signature, descriptor, and Java-side representation of various attributes in the method attribute table, as well as runtime information such as execution permissions. The latter contains only relevant information for performing the method. In the colloquial terms of developers, Reflection is a heavyweight, while MethodHandle is lightweight.

· Since MethodHandle is a simulation of the method instruction call of bytecode, in theory, various optimizations made by the virtual machine in this area (such as method inlining) should be supported by a similar idea on MethodHandle (but currently implemented It is still being improved), and it is almost impossible to directly implement various call point optimization measures by calling methods through reflection.

Call the ancestor method:
MethodHandles.Lookup class is similar to the acquisition of Unsafe class, this class is not new
Insert picture description here

Published 37 original articles · won 6 Like · views 4640

Guess you like

Origin blog.csdn.net/littlewhitevg/article/details/105539789