每一个线程都有自己的栈,栈中的数据都是以栈帧的格式存在的。在这个线程上,正在执行的每一个方法都有各自对应的栈帧,栈帧是一个内存区域,是一个数据集,维系着方法执行过程中的各种数据。
2.JVM直接对java栈的操作只有两个,入栈和出栈。
3.在一条活动的线程中,一个时间点上,只会有一个活动的栈帧,即只有当前正在执行的方法的栈帧是有效的,这个栈帧被称作当前栈帧。定义这个方法的类称作为当前类。执行引擎运行的所有字节码只针对当前栈帧进行操作。如果在该方法中调用了其他方法,对应的新的栈帧会被创建出来,放在栈顶端,成为新的当前栈帧。
PC寄存器中存的是当前栈的地址。
不同的线程中所包含的栈帧是不允许存在相互作用的,即不可能在同一个栈帧之中引用另外一个线程的栈帧。
如果当前方法调用了其他方法,方法返回时,当前栈帧会传回此方法的执行结果给前一个栈帧。接着,虚拟机会丢弃当前栈帧,使得前一个栈帧成为当前栈帧、
java方法有两种返回函数的方式,一种是正常的函数返回。使用return指令。另外一种是抛出异常,不管使用哪种方式,都会导致栈帧被弹出。
public static void main(String[] args) {
method1();
System.out.println("执行完main方法");
}
private static void method1() {
method2();
System.out.println("执行完方法1");
}
private static void method2() {
method3();
System.out.println("执行完方法2");
}
private static void method3() {
int sum = 1 / 0;
System.out.println("执行完方法3");
}
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.fuck996.ExceptionTest.method3(ExceptionTest.java:20)
at com.fuck996.ExceptionTest.method2(ExceptionTest.java:15)
at com.fuck996.ExceptionTest.method1(ExceptionTest.java:10)
at com.fuck996.ExceptionTest.main(ExceptionTest.java:5)
分析:
因为方法3中出现异常,3抛出了方法2,方法2直接抛给了方法1,方法1直接抛给了main方法,main直接抛给了JVM,所以虚拟机直接挂掉了。
如果我们捕获了异常,看看执行结果是如何的。
public static void main(String[] args) {
method1();
System.out.println("执行完main方法");
}
private static void method1() {
method2();
System.out.println("执行完方法1");
}
private static void method2() {
method3();
System.out.println("执行完方法2");
}
private static void method3() {
try {
int sum = 1 / 0;
System.out.println("执行完方法3");
} catch (Exception e) {
e.printStackTrace();
}
}
java.lang.ArithmeticException: / by zero
at com.fuck996.ExceptionTest.method3(ExceptionTest.java:21)
at com.fuck996.ExceptionTest.method2(ExceptionTest.java:15)
at com.fuck996.ExceptionTest.method1(ExceptionTest.java:10)
at com.fuck996.ExceptionTest.main(ExceptionTest.java:5)
执行完方法2
执行完方法1
执行完main方法
栈帧的内部结构:
1.局部变量表:Local Variables
定义为一个数字数组,主要用于存储方法的参数和定义在方法体中的局部变量。在局部变量表中32位以内的类型只占一个slot,包括returnAddress类型。64位的类型(long和double)占用两个slot 。byte,float,char在存储前被转换成int boolean.由于局部变量是线程私有的,不存在数据的安全问题。局部变量表(方法内的)的大小是在编译期间就确定下来的。