-
由于跨平台性的设计,Java的指令都是根据栈来设计的。不同平台CPU架 构不同,所以不能设计为基于寄存器的。
-
优点是跨平台,指令集, 编译器容易实现,缺点是性能下降,实现同样 的功能需要更多的指令。
什么是栈?
存放的是栈帧,对应着一次次方法的调用。存放着基本数据类型和引用类型的地址。
作用
主管Java程序的运行,它保存方法的局部变量 部分结果,并参与方法的 调用和返回。
生命周期
和线程共生
栈桢的内部结构
-
局部变量表(Local Variables)
-
操作数栈(operand stack)
-
动态链接(Dynamic Linking)
-
方法返回地址(Return Address)
局部变量表
局部变量表也被称之为局部变量数组或本地变量表
-
定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量 ,这些数据类型包括各类基本数据类型、对象引用(reference),以及 returnAddress类型。
-
由于局部变量表是建立在线程的栈上,是线程的私有数据,因此不存在数据 安全问题
-
局部变量表所需的容量大小是在编译期确定下来的,并保存在方法的code 属性的maximum local variables数据项中。在方法运行期间是不会改 变局部变量表的大小的。
变量槽
局部变量表,最基本的存储单元是slot(变量槽)
long和double占两个变量槽,其余占一个。
静态方法中没有this,非静态方法中this占变量槽的首位,索引为0的位置
操作数栈
-
主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间
-
操作数栈就是JVM执行引擎的一个工作区,当一个方法刚开始执行的时候,一个新的栈帧也会随之被创建出来,这个方法的操作数栈是空的。
-
每一个操作数栈都会拥有一个明确的栈深度用于存储数值,其所需的最大深度在编译期就定义好了,保存在方法的Code属性中,为max_stack的值。
-
栈中的任何一个元素都是可以任意的Java数据类型。
32bit的类型占用一个栈单位深度
64bit的类型占用两个栈单位深度
-
操作数栈并非采用访问索引的方式来进行数据访问的,而是只能通过标准的入栈(push)和出栈(pop)操作来完成一次数据访问。
指向运行时常量池的方法引用(动态链接)
-
每一个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用。包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接(Dynamic Linking)。比如:invokedynamic指令
-
在Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用(Symbolic Reference)保存在class文件的常量池里。 比如:描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的,那么动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用。
为什么需要常量池呢?
常量池的作用,就是为了提供一些符号和常量,便于指令的识别。
方法的调用
-
在JVM中,将符号引用转换为调用方法的直接引用与方法的绑定机制相关。
静态链接:
-
当一个字节码文件被装载进JVM内部时,如果被调用的目标方法在编译期可知, 且运行期保持不变时。这种情况下将调用方法的符号引用转换为直接引用的 过程称之为静态链接。
动态链接:
-
如果被调用的方法在编译期无法被确定下来,也就是说,只能够在程序运行 期将调用方法的符号引用转换为直接引用,由于这种引用转换过程具备动态 性,因此也就被称之为动态链接。