介绍
Java虚拟机栈(Java Virtual Machine Stack,简称JVM Stack)是Java虚拟机的一个组成部分,它用于存储方法的局部变量、操作数栈以及动态链接和方法出口信息。JVM在执行Java程序时,每个线程都会有一个私有的JVM栈,用于支持线程的Java方法的执行。
位置
Java虚拟机栈存储在运行时数据区,是线程私有的。
内部构成
Java虚拟机栈一般有三个部分组成,他们分别是局部变量表,操作数栈和帧数据。
局部变量表
介绍
Java虚拟机栈中的局部变量表(Local Variable Table)是一个非常重要的组成部分,它用于存储方法执行过程中的局部变量。
关键特性
-
存储局部变量:局部变量表用于存储方法中的局部变量,包括方法参数和方法内部定义的变量。
-
索引引用:局部变量表中的每个变量都通过索引来引用。索引从0开始,第一个参数的索引是0,第二个参数的索引是1,以此类推。方法内部定义的局部变量索引紧接着参数索引。
-
作用域:局部变量的作用域仅限于定义它的代码块。当方法执行完毕,局部变量表也随之销毁,局部变量将不再可用。
-
数据类型:局部变量表可以存储Java中的各种基本数据类型(如int, float, double等)和对象引用。
-
存储数量:局部变量表的大小由JVM在编译时确定,与方法的局部变量数量和类型有关。
-
生命周期:局部变量的生命周期与方法的执行过程相对应。当方法调用开始时,JVM会为这个方法调用创建一个新的栈帧,并初始化局部变量表。当方法调用结束时,局部变量表也会随之销毁
-
性能优化:由于局部变量表存储在线程的JVM栈中,访问速度非常快,有助于提高程序的执行效率。
操作数栈
介绍
操作数栈(Operand Stack)是Java虚拟机(JVM)中的一种数据结构,用于支持方法执行期间的字节码指令操作。操作数栈遵循后进先出(Last In First Out, LIFO)的原则,即最后压入栈的元素最先被取出。以下是操作数栈的一些关键特性。
关键特性
-
存储中间数据:操作数栈会在执行字节码指令过程中存放临时数据,计算结果或者计算中产生的结果。
-
指令操作:操作数栈支持指令操作,例如当执行 3 * 2 操作时会先从操作数栈中取出2 然后 取出3 接着执行 3 * 2 。并将最后的结果再次存入操作数栈。(力扣的逆波兰表达式求值与次类似)
-
深度大小:与局部变量表相同,操作数栈的大小也在JVM编译时已经确定,与执行方法中的最大操作数的数量有关。
-
生命周期:操作数栈的声明周期和局部变量表相同,都是与方法的执行过程相对应,当方法开始执行时,操作数栈被初始化为空,当方法结束时操作数栈被清空。
帧数据
介绍
帧数据中一般包含动态链接(指向方法区中运行时常量池中的引用):用于支持方法调用过程中的动态链接。 方法出口:包含方法调用的返回地址,用于在方法执行完毕后返回到调用方法的适当位置。
关键特性
-
.局部变量表的存储:JVM栈对每一个方法都形成了一个栈帧,用于存储方法的局部变量和临时数据。
public class Main {
public static void main(String[] args) {
test1();
}
public static void test1() {
System.out.println("今天周二");
System.out.println("************************");
test2();
}
public static void test2() {
System.out.println("明天周三");
}
}
2.Java虚拟机栈是线程私有的:因此不会发生垃圾回收(垃圾回收一般是堆中进行的),Java虚拟机栈随着线程的创建而创建,随着线程的销毁而销毁,因为是线程私有的,因此不会发生线程安全问题且天生是线程隔离的。
3.溢出 :当JVM栈的深度过大,可能会导致溢出,会导致报错StackOverflowError,一般这种情况就是发生了递归。
public class Main {
public static void main(String[] args) {
test1();
}
public static void test1() {
System.out.println("今天周二");
test1();
}
}
了解JVM栈对于优化程序性能、诊断和解决内存溢出等问题非常重要。