JVM虚拟机栈结构和功能

Java虚拟机栈

JVM虚拟机栈是线程私有的,每个线程都具有一个虚拟机栈,其内部保存一个个栈帧,对应着每个方法的调用。生命周期和线程生命周期相同。

虚拟机栈作用

主管java程序的运行,进入的栈帧存储着局部变量表,操作数栈,动态连接,方法返回值等。

栈帧:

栈帧是虚拟机栈的基本单位,栈帧的调入对应着方法的调用,栈帧的弹出对应着方法的结束返回,其中,由于只有弹栈和入栈的操作,java虚拟机栈没有GC机制,但在栈空间不够时会出现StackOflowError错误,java虚拟机允许栈的大小是动态或者固定不变的(可通过-Xss命令实现栈的扩展),尽管可以通过命令调整栈大小,但不能无限制扩展,当栈无法申请到足够的内存会抛出OutOfMemoryError错误。

运行时栈帧结构

JVM虚拟机栈结构和功能_第1张图片

JVM虚拟机栈结构和功能_第2张图片
JVM虚拟机栈结构和功能_第3张图片

栈帧包括局部变量表,操作数栈,动态连接,方法返回地址和一些附加信息。其中局部变量表和操作数栈最为重要。

局部变量表

局部变量表定义为一个数字数组,用于存储方法参数和定义在方法体内的局部变量,这些数据类型包括基本数据类型,引用数据类型,及returnnAddress类型。

由于局部变量表建立在线程的栈上,因此不存在数据安全问题。

局部变量表所需容量大小在编译器确定下来,并保存在方法的Code属性的max local variables数据项中,方法运行期间不会改变局部变量表大小。

局部变量表中的变量只在当前方法中有效,方法调用结束后,随着方法栈帧的弹出,局部变量表随之销毁。
JVM虚拟机栈结构和功能_第4张图片

槽(slot)
  1. 变量槽是局部变量表中的最小单位。槽没有明确的内存空间大小, char,byte,int,short,float,引用类型和returnAddress占1个槽,而double和long类型占2个槽。
  2. JVM会为每个槽都分配一个访问索引,通过这个索引便可以访问到局部变量表中指定的局部变量。
  3. 当一个方法被调用时,方法的参数和方法体内的局部变量将会按顺序复制到局部变量表的slot上
  4. 如果访问占2个槽的局部变量,只需使用第一个槽的索引
  5. 如果当前栈是由构造方法或者实例方法创建的,那么该对象引用this将放在index为0的slot处,其余参数继续列。
  6. 局部变量表中的变量是垃圾回收根节点,只要被局部变量表中直接或间接引用的对象不会被回收。
    JVM虚拟机栈结构和功能_第5张图片

操作数栈

在方法执行过程中,根据字节码指令,往栈中写入数据或提取数据。操作数栈是JVM执行引擎的一个工作区,当一个方法开始执行时,一个新的栈帧也会被创建。

每个操作数栈都有个明确的深度用于存储,深度在编译期间就确定好了,保存在方法的Code属性中,为max_stack的值。

操作数栈中,32bit的占1一个栈,64bit的占2个栈。

 public static void add(java.lang.String, int, int, java.util.List);
Code:
   0: iload_1
   1: ifne          20
   4: iload_2
   5: ifne          20
   8: aload_3
   9: aload_0
  10: invokevirtual #13                 // Method java/lang/String.toString:()Ljava/lang/String;
  13: invokeinterface #14,  2           // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z

动态链接

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用。引用的目的是为了实现动态链接。

在java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用,保存在class文件的常量池里。
JVM虚拟机栈结构和功能_第6张图片

方法返回地址

方法执行后,有2种方法退出:

1.正常执行并返回
2.方法产生异常,异常并未处理

无论哪种方法退出,在方法退出后都返回该方法的被调用的位置,方法正常退出时,调用方法的PC计数器的值作为返回地址,通过异常退出的,返回地址要通过异常表来确定,栈帧中一般不会保存这部分信息,并不会给上层调用者产生任何返回值。

以上时鉴于阅读《深入理解JAVA虚拟机》的第8章时的理解,本文章只为了记录自己的学习心得,有不对的地方欢迎留言指出。

你可能感兴趣的:(java,jvm,栈)