java运行时数据区-虚拟机栈详解

1.运行时数据区模型

在这里插入图片描述
在这个图中,灰色的表示每个线程独有,红色的表示线程共享

2.虚拟机栈的特点

java虚拟机栈早期也叫java栈,每个线程在创建时都会创建一个虚拟机栈,如图所示
在这里插入图片描述
多个线程所对应的栈组成了我们所说的java栈,说到栈,就不得不提栈帧栈帧是java虚拟机最基本的执行单元,一个栈帧对应一个方法,栈帧存储了方法的局部变量表,操作数栈,动态链接和方法返回地址等信息,文章后面会一一介绍,虚拟机栈的生命周期和线程一致,栈的特点:

  • 快速有效的存储方式,访问速度仅次于pc寄存器
  • jvm对栈的操作:入栈出栈,即栈帧的使用和卸载的过程,先进后出,后进先出
  • 不存在垃圾回收问题

补充:在一个java程序中,在同一时刻,同一线程,只有位于栈顶的栈帧才是有效的,被称为当前栈帧,栈帧的结构:
在这里插入图片描述

2.1栈运行原理

  • 由于栈是线程独有,所以不同的线程间的栈帧不允许存在相互引用,即不可以在一个栈帧中引用另外一个线程的栈帧
  • 当前方法调用了其他方法,那么被调用的方法返回之际,当前栈帧会传回此方法的执行结果给调用它的线程,然后虚拟机会执行出栈操作,丢弃当前栈帧,让前一个栈帧继续充当当前栈帧
  • java中有两种返回函数的方式,一是程序正常结束,使用return指令(注意,这里的return是虚拟机内部给每个方法都会加上的关键字,表示这个方法正常结束,和我们显示的写不写return没有关系),二是抛出异常,两种方式都会导致当前栈帧被弹出

我们通过代码来演示当程序抛出异常会产生什么结果

public class stackTest {
    public static void main(String[] args) {
        stackTest s = new stackTest();
        s.method1();
    }
    void method1(){
        System.out.println("method1 start......");
        method2();
        System.out.println("method1 end......");
    }
    void method2(){
        System.out.println("method2 start......");
        method3();
        System.out.println("method2 end......");
        //System.out.println(10 / 0);
    }
    void method3(){
        System.out.println("method3 start......");
        System.out.println("method3 end......");
    }
}

先把程序出现异常的地方注释。看正常结果

method1 start......
method2 start......
method3 start......
method3 end......
method2 end......
method1 end......

然后打印错误的结果:

method1 start......
method2 start......
method3 start......
method3 end......
method2 end......
Exception in thread "main" java.lang.ArithmeticException: / by zero

为什么会出现这种结果呢?首先method2发生了异常,抛给method1,但是方法1没有捕获异常,所以程序没有正常结束,我们只需要捕获这个异常就可以使栈帧正常被压入,弹出

void method1(){
        System.out.println("method1 start......");
        try {
            method2();
        } catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("method1 end......");
    }

输出结果:

method1 start......
method2 start......
method3 start......
method3 end......
method2 end......
method1 end......
java.lang.ArithmeticException: / by zero

2.2局部变量表(local variables table)

局部变量表是一组变量值的存储空间用来存储方法参数和定义在方法内的局部变量,我们可以这样来理解这个表:一个一维数组,一个方法的调用即实参到形参的传递就是通过局部变量表完成,局部变量表以变量槽(slot) 为最小单位,这个变量槽都能放入一个

  • boolean
  • int
  • float
  • char
  • short
  • byte
  • 对象引用reference
  • returnAddress
  • double
  • long

这8种数据类型,其中32位类型的占用一个slot,64位类型的占用两个slot,由于局部变量表存在栈中,因此不存在数据安全问题,需要注意的是:局部变量表的大小是在编译器确定下来的并且保存在方法的code属性的maximum local variables数据项中,运行期间不会改变,我用我的idea做演示
附上我的代码:

public static void main(String[] args) {
        stackTest s = new stackTest();
        s.method1();
}

在这里插入图片描述
可以和清楚的看见,main方法中确实只有两个变量,s和args,长度为2,
在这里插入图片描述

追加:关于Slot的理解和jclasslib的使用请参考关于Slot的理解和jclasslib的使用

文章到这个位置我们就可以解决之前一个问题了:

类变量和实例变量在使用之前都会在类加载阶段赋予默认值,但局部变量无法赋默认值,必须显示赋值,否则编译不通过,其原因就是:局部变量表中存储了这个变量的值,基本数据类型就存值,引用类型就存的是地址,本质上还是值,所以局部变量表不能为空

2.3操作数栈(Operand Stack)

解释:在方法执行过程中,根据字节码指令,往栈中写入数据或提取数据,即入栈(push)/出栈(pop)
java虚拟机的解释引擎是基于栈的执行引擎,这里的栈就是操作数栈

说明:文章会持续更新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值