JVM---栈帧

Java 虚拟机栈中的栈帧

在这里插入图片描述

上图描述的是某个Java 虚拟机栈的情况,在某个Java 虚拟机栈中可能存在多个栈帧。栈帧(Frame)是用来存储数据和部分过程结果的数据结构,同时也用来处理动态链接、方法返回值和异常分派。
栈帧随着方法的调用而创建,随着方法的结束而销毁,每一个栈帧都是对已调用方法的运行情况的记录。栈帧的存储空间由创建它的线程分配在Java 虚拟键栈中,每一个栈帧的基本组成部分有:

 - 本地变量表(局部变量表)
 - 操作数栈
 - 指向当前方法所属类的运行时常量池的引用
 - 其他信息

在某个线程运行过程中可能会有多个栈帧,只有当前正在执行的那个方法的栈帧是活动的,这个栈帧称为当前栈帧,这个栈帧对应的方法称为当前方法,定义这个方法的类称为当前类。对局部变量表和操作数栈的各种操作都是针对当前栈帧的局部变量表和操作数栈,局部变量表和操作数栈是提供给Java 虚拟机来使用的,也就是说,使用Java 虚拟机指令来操作局部变量表和操作数栈。

1.局部变量表

顾名思义,局部变量表就是用来保存当前方法中的局部变量的一张表,所以局部变量表的大小在编译期就是可以确定的。那一个局部变量具体又是什么呢?
一个局部变量可以保存一个类型为 boolean、byte、char、short、int、float、reference的数据,两个连续的局部变量可以保存一个类型为 long、double的数据,显然,一个局部变量所占的空间为4个字节,对于不足4个字节的数据,则会自动提升为int 型。
在这里插入图片描述
如上图所示,方法的局部标量按照这样的顺序在局部变量表中从排列下来(Java中数据字节序为大端序,Java 虚拟机中对64位的数据也不需要进行对齐)。局部变量表就像一个数组,所以访问某个局部变量时我们就可以使用变量的下标来访问,首个局部变量的索引值为0,占据两个局部变量的数据的索引则采用所占用的两个局部变量中索引值较小的值。
Java 虚拟机使用局部变量表来完成方法调用时的参数传递,对于调用类方法和实例方法,局部变量表有一点区别。当调用类方法时,方法的参数会依次传递到局部变量表中从0开始的连续位置上;当调用实例方法时,第0个局部变量存放的一定是该实例方法所在对象的引用(即我们常用的 this 指针),后续的其他参数则会传递到局部变量表中从1开始的连续位置上

2.操作数栈

操作数栈具有栈的特点,先进后出。栈帧中操作数栈的最大深度由编译器决定。
在栈帧刚刚创建时,操作数栈是一个空栈。Java 虚拟机提供了字节码指令来从局部变量表或者对象实例的字段中复制常量或变量到操作数栈中,也提供了指令用于从操作数栈中取走数据、操作数据以及把操作结果重新入栈。在调用方法时,操作数栈也用来准备调用方法的参数和接收方法返回的结果。
例如有这样一段代码:

int i ;
i++;

实际的字节码指令大概是这样的:

iconst_0		//	将常量0压入操作数栈
istore_1		//  将栈中的操作数弹出,放在索引为1的局部变量中,即将0赋给i
iload_1		    //  将变量1压入到操作数栈中
iconst_1		//  将常量1压入操作数栈
iadd 			//	从操作数栈中弹出两个数,相加后入栈
istore_1		//	从栈中弹出一个数并放在i中

从上面一段代码可以看出,我们用java写的代码被java编译器翻译成字节码后,我们在方法中声明、定义的变量存放在局部变量表中,当对变量进行操作时,就需要频繁地出栈、入栈。

3.运行时常量池的引用

每个栈帧中都包含一个指向当前方法所在类的运行时常量池的引用,以此对当前方法的代码实现动态链接。在class 文件里,一个方法若要调用其他方法,或者访问成员变量,则需要将符号引用所表示的方法转换为对实际方法的直接引用。

猜你喜欢

转载自blog.csdn.net/Miha_Singh/article/details/88722689