当前位置:网站首页>jvm 二之 栈帧内部结构

jvm 二之 栈帧内部结构

2022-08-02 06:29:00 兮家小二

栈帧内部结构

1、内存结构

在每一个方法 栈帧中都会有自己独立的,其中包括

  • 1、局部变量表 (存放当前方法对应的局部变量);
  • 2、操作数栈 (或表达式栈);
  • 3、动态连接 (或指向运行时常量池的方法引用)
  • 4、方法出口 (或方法正常退出或者异常退出的定义)

栈帧包含方法的所有信息
使用 javap -v Demo03.class 查看转成汇编后的源码

在这里插入图片描述

2、idea 分析插件

打开idea 中的settings > plugins 搜索 jclasslib 插件
重启后点击 view > 选择 show bytecode with jclasslib

在这里插入图片描述
查看分析
在这里插入图片描述

一、局部变量表

局部变量表中的变量只在 当前方法 调用中有效。
在方法执行时,虚拟机通过使用局部变量表完成参数值到参数变量列表的传递过程。
当方法调用结束后,随着方法栈帧的销毁,局部变量表也会随之销毁。

每一个方法都有一个局部变量表

1.1、Slot(变量槽–index)

public static void main(String[] args) {
    String str = "mayikt";
    int j = 20;
    double d = 66.66;
    boolean b = true;
}

如下: JVM会为局部变量表中每一个变量分配变量槽

  • args 参数: 在 局部变量表中 slot 索引 0 的位置
  • str 参数: 在 局部变量表中 slot 索引 1 的位置
  • j 参数: 在 局部变量表中 slot 索引 2 的位置
  • d 参数: 在 局部变量表中 slot 索引 3 的位置 (double 会占用两个槽位)
  • b 参数: 在 局部变量表中 slot 索引 5 的位置

在这里插入图片描述

局部变量表最基本的存储单元就是变量槽。

  • 32位以内的类型只占用一个slot (包括returnAddress类型),
  • 64位的类型(long和double)占用两个slot。
  • short,byte,boolean 等数据也占用一个变量槽,因为jvm会在存储时将上述变量转为int类型(变量槽是最基本存储单元,无法分割,只能整个使用)。

1.2、变量槽的复用

public static void mayikt002() {
    int a = 0;
    {
        int b=30;
        System.out.println("mayikt");
    }
    int c = 0;
}

此处原因就是JVM对变量槽有一个复用性为

  • 变量b 使用了变量槽 1, 超出其作用域后不再生效,
  • 变量c 发现变量槽 1 其作用域后超出, 直接占据了 b 的位置

在这里插入图片描述

1.3、变量槽中的 this

如果当前的方法是实例方法或者是构造方法 则jvm默认会在局部变量表中创建 一个
当前对象 this , 存入在我们当前方法对应的局部表第0个位置 这样我们就可以在实例方法中 使用 this,静态方法不会。

public void mayikt() {
    int j = 20;
}

变量槽中默认存在 this 变量

在这里插入图片描述

二、操作数栈

mian 调用 a, a 调用 b ,b 调用c , c调用 d, 方法会压入到栈空间中
而操作数栈是值变量的操作进行的压栈操作,从而可以执行相关 加减乘除 操作

2.1、基础操作 指令解析

当前代码

public int compute() {
    int a = 10;
    int b = 20;
    int c = (a + b) * 10;
    return c;
}

转汇编后的代码 , javap -v xxxx.class

在这里插入图片描述

         0: bipush        10              //  将一个8位带符号整数压入栈
         2: istore_1                      //  变量槽 1   a 存入 10      
         3: bipush        20              //  将一个8位带符号整数压入栈        
         5: istore_2                      //  变量槽 2   b 存入 20  
         6: iload_1                       //  获取槽1  变量a=10;
         7: iload_2                       //  获取槽2  变量a=20;
         8: iadd                          //  i执行int类型的加法 10+20=30
         9: bipush        10              //  将一个8位带符号整数压入栈 10
        11: imul                          //  imul 执行int类型的乘法 30*10=300
        12: istore_3                      //  变量槽3  c存入计算结果 300 
        13: iload_3                       //  获取槽3  变量c=300;
        14: ireturn                       //  方法返回最后读取的变量槽数据

2.2、i++ 和 ++i 解析

i++

执行+1 前压入的栈数据, 栈顶数据是 0
在这里插入图片描述
++i

执行+1 后压入的栈数据, 栈顶数据是 1
在这里插入图片描述

2.3、栈溢出

StackOverflowError(栈溢出)
StackOverflowError代表的是,当栈深度超过虚拟机分配给线程的栈大小时就会出现此error

默认是有最大深度的, 常出现在递归调用中

2.4、方法出口

方法返回地址

2.5、动态链接

在操作数栈中找到被调用的子方法就是动态链接

动态链接: 每个栈帧都保存了 一个可以指向当前方法所在类的 运行时常量池,
目的是: 当前方法中如果需要调用其他方法的时候, 能够从运行时常量池中找到对应的符号引用, 然后将符号引用转换为直接引用,然后就能直接调用对应方法, 这就是动态链接

三、常量池

什么是常量池(Constant Pool)
java经过编译后生成的.class文件,是Class文件的资源仓库。

常量池分类:

    1. 运行时常量池 ,jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在元空间中,我们常说的常量池,就是指方法区中的运行时常量池
    1. 静态常量池 class文件的关键信息,类和接口的全限定名字段的名称和描述符方法的名称和描述符,以及被 final 修饰的常量值等,如 数量值字符串值 以及字面量(Literal)和符号引用(Symbolic References) ,如: 类引用字段引用方法引用
    1. 字符串常量池 顾名思义,存放字符串, 如: Strng, StringBufferStringBuffer 等, jdk7 之前 方法区JDK7 存放在堆中JDK 8方法区改 元空间 字符串常量池还是存放在我们堆中

在这里插入图片描述

元空间就是之前的方法区
在这里插入图片描述

原网站

版权声明
本文为[兮家小二]所创,转载请带上原文链接,感谢
https://xijia.blog.csdn.net/article/details/126081614