JVM stack frame

JVM stack frame

 First, the stack frame

 

The stack frame (Frame) is a data structure used to store data and partial process results, and is also used to handle dynamic linking (Dynamic Linking), method return values ​​and exception dispatch (Dispatch Exception).

 

Stack frames are created with method invocations and destroyed with method termination—whether the method completes normally or abnormally (throws an uncaught exception within the method) that counts as method termination. The storage space of the stack frame is allocated in the Java virtual machine stack, and each stack frame has its own local variable table, operand stack and a reference to the runtime constant pool of the class to which the current method belongs.

 

The capacity of the local variable table and operand stack is determined at compile time, and is saved and provided to the stack frame through the Code attribute of the method. Therefore, the size of the stack frame size only depends on the implementation of the Java virtual machine and the memory that can be allocated when the method is called.

 

Within a thread, only the stack frame of the currently executing method is active. This stack frame is called the current stack frame (Current Frame), the method corresponding to this stack frame is called the current method (Current Method), and the class that defines this method is called the current class (Current Class). Various operations on the local variable table and the operand stack usually refer to the operations on the local variable table and the operand stack on the current stack frame.

 

If the current method calls other methods, or the execution of the current method ends, the stack frame of this method is no longer the current stack frame. When a new method is called, a new stack frame is also created and becomes the new current stack frame as program control is transferred to the new method. When the method returns, the current stack frame will return the execution result of this method to the previous stack frame. After the method returns, the current stack frame will be discarded, and the previous stack frame will become the current stack frame again.

 

The stack frame is thread-local private data, it is impossible to reference the stack frame of another thread in one stack frame.

 

 

Second, the local variable table

 

Each stack frame contains a set of variable lists called Local Variables . The length of the local variable table in the stack frame is determined at compile time, and is stored in the binary representation of the class and interface, which is saved and provided to the stack frame through the Code attribute of the method.

 

A local variable (Slot) can hold data of type boolean, byte, char, short, float, reference and returnAddress, and two local variables can hold data of type long and double.

 

Local variables use indexing for positioning access. The index value of the first local variable is zero, and the index value of local variables is all integers from zero to less than the maximum capacity of the local variable table.

 

Data of type long and double occupy two consecutive local variables , and data values ​​of these two types are located using the smaller index value of the two local variables . For example, when we say that a value of type double is stored in a local variable with an index value of n, it actually means that both local variables with an index value of n and n+1 are used to store this value. The local variable with index value n+1 cannot be read directly, but it may be written, but if this operation is performed, the content of the local variable n will be invalidated.

 

The n value of the local variable n mentioned above is not required to be an even number, and the Java virtual machine does not require double and long type data to be stored in continuous local variables in 64-bit alignment. The virtual machine implementer is free to choose the appropriate way to store a value of type double or long through two local variables.

 

The Java virtual machine uses the local variable table to complete the parameter transfer when the method is called. When a method is called, its parameters will be passed to the continuous local variable table position starting from 0. In particular, when an instance method is called, the 0th local variable must be used to store a reference to the object where the called instance method is located (ie, the "this" keyword in the Java language). Subsequent additional arguments will be passed to consecutive local variable table locations starting at 1.

 

Third, the operand stack

 

Each stack frame contains a Last-In-First-Out (LIFO) stack called the Operand Stack. The length of the operand stack in the stack frame is determined at compile time, and is stored in the binary representation of the class and interface, which is saved and provided to the stack frame through the Code attribute of the method.

 

Under the premise that the context is clear and there will be no misunderstanding, we often refer to the "operand stack of the current stack frame" simply as the "operand stack".

 

操作数栈所属的栈帧在刚刚被创建的时候,操作数栈是空的。Java虚拟机提供一些字节码指令来从局部变量表或者对象实例的字段中复制常量或变量值到操作数栈中,也提供了一些指令用于从操作数栈取走数据、操作数据和把操作结果重新入栈。在方法调用的时候,操作数栈也用来准备调用方法的参数以及接收方法返回结果。

 

举个例子,iadd字节码指令的作用是将两个int类型的数值相加,它要求在执行的之前操作数栈的栈顶已经存在两个由前面其他指令放入的int型数值。在iadd指令执行时,2个int值从操作栈中出栈,相加求和,然后将求和结果重新入栈。在操作数栈中,一项运算常由多个子运算(Subcomputations)嵌套进行,一个子运算过程的结果可以被其他外围运算所使用。

每一个操作数栈的成员(Entry)可以保存一个Java虚拟机中定义的任意数据类型的值,包括long和double类型。

 

在操作数栈中的数据必须被正确地操作,这里正确操作是指对操作数栈的操作必须与操作数栈栈顶的数据类型相匹配,例如不可以入栈两个int类型的数据,然后当作long类型去操作他们,或者入栈两个float类型的数据,然后使用iadd指令去对它们进行求和。有一小部分Java虚拟机指令(例如dup和swap指令)可以不关注操作数的具体数据类型,把所有在运行时数据区中的数据当作裸类型(Raw Type)数据来操作,这些指令不可以用来修改数据,也不可以拆散那些原本不可拆分的数据,这些操作的正确性将会通过Class文件的校验过程来强制保障。

 

在任意时刻,操作数栈都会有一个确定的栈深度,一个long或者double类型的数据会占用两个单位的栈深度,其他数据类型则会占用一个单位深度。

 

 

分析:

有如下代码:

 

Java代码 
  1. package cc.lixiaohui.demo;  
  2. public class Foo {  
  3.     public static void main(String[] args) {  
  4.         int a = 1;  
  5.         int b = 2;  
  6.         int c = a + b;  
  7.     }  
  8. }  

 利用javap工具生成虚拟机汇编代码(java虚拟机指令集):

 

 括号内的注释我是自己加的,其余是javap生成的

Java代码 
  1. E:\EclipseWorkspace\demo-foo\target\classes\cc\lixiaohui\demo>javap -c -l Foo.class  
  2. Compiled from "Foo.java"  
  3. public class cc.lixiaohui.demo.Foo {  
  4.   public cc.lixiaohui.demo.Foo();   (//这是默认构造方法)  
  5.     Code:  
  6.        0: aload_0  
  7.        1: invokespecial #8                  // Method java/lang/Object."<init>":()V  
  8.        4return  
  9.     LineNumberTable:  
  10.       line 30  
  11.     LocalVariableTable:  
  12.       Start  Length  Slot  Name   Signature  
  13.              0       5     0  this   Lcc/lixiaohui/demo/Foo;  
  14.   
  15.   public static void main(java.lang.String[]);  
  16.     Code:  
  17.        0: iconst_1     (//把int值1压入操作数栈)  
  18.        1: istore_1     (//把栈顶值存储到局部变量表下标为1的位置)  
  19.        2: iconst_2     (//把int值2压入操作数栈)  
  20.        3: istore_2     (//把栈顶值存储到局部变量表下标为2的位置)  
  21.        4: iload_1      (//取局部变量表中下标为1的变量压栈)  
  22.        5: iload_2      (//取局部变量表中下标为2的变量压栈)  
  23.        6: iadd         (//从操作数栈中弹出两个int值进行相加操作,相加的结果压栈)  
  24.        7: istore_3     (//把栈顶值存储到局部变量表下标为3的位置)  
  25.        8return  
  26.     LineNumberTable: (//这是java代码行与该栈帧指令代码行的映射)  
  27.       line 50        (//java代码第五行为“int a = 1”,对应的虚拟机汇编代码为“iconst_1”)  
  28.       line 62  
  29.       line 74  
  30.       line 88  
  31.     LocalVariableTable:  
  32.       Start  Length  Slot  Name   Signature  
  33.              0       9     0  args   [Ljava/lang/String;  
  34.              2       7     1     a   I  
  35.              4       5     2     b   I  
  36.              8       1     3     c   I  
  37. }  

 可以看到执行过程中不断在局部变量表和操作数栈间来回传递数据。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326186458&siteId=291194637