【jvm】虚拟机栈之局部变量表

目录

        • 一、说明
        • 二、代码分析
          • 2.1 代码示例
          • 2.2 执行javap
          • 2.3 jclasslib插件查看
        • 三、对slot的理解
          • 3.1 说明
          • 3.2 slot索引图
          • 3.3 实例方法的局部变量表
          • 3.4 long和double类型变量占2个slot
        • 四、slot的重复利用
          • 4.1 说明
          • 4.2 变量c复用变量b的槽位
        • 五、静态变量与局部变量对比

一、说明
  • 1.Local Variables
  • 2.局部变量表也被称为局部变量组或本地变量表
  • 3.定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量,这些数据类型包括各类基本数据类型、对象引用(reference),以及returnAddress类型
  • 4.由于局部变量表是建立在线程的栈上,是线程的私有数据,因此不存在数据安全问题
  • 5.局部变量表所需的容量大小是在编译期确定下来的,并保存在方法的Code属性的maximum local variables数据项中。在方法运行期间是不会改边局部变量表的大小的,因为在java代码编译后就已经确定了
  • 6.方法嵌套调用的次数由栈的大小决定。一般来说,栈越大,方法嵌套调用次数越多。对一个函数而言,它的参数和局部变量越多,使得局部变量表膨胀,它的栈帧就越大,以满足方法调用所需传递的信息增大的需求,函数调用就会占用更多的栈空间,导致其嵌套调用次数就会减少
  • 7.局部变量表中的变量只在当前方法调用中有效。在方法执行时,虚拟机通过使用局部变量表完成参数值到参数变量列表的传递过程。当方法调用结束后,随着方法栈帧的销毁,局部变量表也随之销毁
二、代码分析
2.1 代码示例
package com.learning.stack.local_variables;

/**
 * @Author wangyouhui
 * @Description 局部变量表测试
 **/
public class LocalVariableTest {
    public static void main(String[] args) {
        LocalVariableTest localVariableTest = new LocalVariableTest();
        int num = 10;
        localVariableTest.print();
    }

    private void print() {
        System.out.println("局部变量表");
    }
}
2.2 执行javap
  • 1.执行javap -v StackOverflowErrorLearning.class
Classfile /F:/jdk-learning/jvm/target/classes/com/learning/stack/local_variables/LocalVariableTest.class
  Last modified 2023-10-16; size 785 bytes
  MD5 checksum e16aaaceec49515090d5cb15e4c6467a
  Compiled from "LocalVariableTest.java"
public class com.learning.stack.local_variables.LocalVariableTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #8.#26         // java/lang/Object."":()V
   #2 = Class              #27            // com/learning/stack/local_variables/LocalVariableTest
   #3 = Methodref          #2.#26         // com/learning/stack/local_variables/LocalVariableTest."":()V
   #4 = Methodref          #2.#28         // com/learning/stack/local_variables/LocalVariableTest.print:()V
   #5 = Fieldref           #29.#30        // java/lang/System.out:Ljava/io/PrintStream;
   #6 = String             #31            // 局部变量表
   #7 = Methodref          #32.#33        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #8 = Class              #34            // java/lang/Object
   #9 = Utf8               
  #10 = Utf8               ()V
  #11 = Utf8               Code
  #12 = Utf8               LineNumberTable
  #13 = Utf8               LocalVariableTable
  #14 = Utf8               this
  #15 = Utf8               Lcom/learning/stack/local_variables/LocalVariableTest;
  #16 = Utf8               main
  #17 = Utf8               ([Ljava/lang/String;)V
  #18 = Utf8               args
  #19 = Utf8               [Ljava/lang/String;
  #20 = Utf8               localVariableTest
  #21 = Utf8               num
  #22 = Utf8               I
  #23 = Utf8               print
  #24 = Utf8               SourceFile
  #25 = Utf8               LocalVariableTest.java
  #26 = NameAndType        #9:#10         // "":()V
  #27 = Utf8               com/learning/stack/local_variables/LocalVariableTest
  #28 = NameAndType        #23:#10        // print:()V
  #29 = Class              #35            // java/lang/System
  #30 = NameAndType        #36:#37        // out:Ljava/io/PrintStream;
  #31 = Utf8               局部变量表
  #32 = Class              #38            // java/io/PrintStream
  #33 = NameAndType        #39:#40        // println:(Ljava/lang/String;)V
  #34 = Utf8               java/lang/Object
  #35 = Utf8               java/lang/System
  #36 = Utf8               out
  #37 = Utf8               Ljava/io/PrintStream;
  #38 = Utf8               java/io/PrintStream
  #39 = Utf8               println
  #40 = Utf8               (Ljava/lang/String;)V
{
  public com.learning.stack.local_variables.LocalVariableTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."":()V
         4: return
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/learning/stack/local_variables/LocalVariableTest;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #2                  // class com/learning/stack/local_variables/LocalVariableTest
         3: dup
         4: invokespecial #3                  // Method "":()V
         7: astore_1
         8: bipush        10
        10: istore_2
        11: aload_1
        12: invokespecial #4                  // Method print:()V
        15: return
      LineNumberTable:
        line 9: 0
        line 10: 8
        line 11: 11
        line 12: 15
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      16     0  args   [Ljava/lang/String;
            8       8     1 localVariableTest   Lcom/learning/stack/local_variables/LocalVariableTest;
           11       5     2   num   I
}
SourceFile: "LocalVariableTest.java"
  • 2.图示说明
2.3 jclasslib插件查看
  • 1.字节码
    【jvm】虚拟机栈之局部变量表_第1张图片

  • 2.异常表
    【jvm】虚拟机栈之局部变量表_第2张图片

  • 3.杂项
    【jvm】虚拟机栈之局部变量表_第3张图片

  • 4.slot槽位
    【jvm】虚拟机栈之局部变量表_第4张图片

三、对slot的理解
3.1 说明
  • 1. 参数值的存放总是在局部变量数组的index0开始,到数组长度-1的索引结束
  • 2. 局部变量表,最基本的存储单元是slot(变量槽)
  • 3. 局部变量表中存放编译器可知的各种基本数据类型(8种),引用类型(reference),returnAddress类型的变量
  • 4.在局部变量表里,32位以内的类型只占用一个slot(包括returnAddress类型),64位的类型(long和double)占用两个slot
  • 5.byte、short、char在存储前被转换为int,boolean也被转为int,0表示false,非0表示true
  • 6. long和double则占据两个slot
  • 7. float占1个slot
  • 8. 引用类型也是32位,占1个slot
  • 9. jvm会为局部变量表中的每一个slot都分配一个访问索引,通过这个索引即可成功访问到局部变量表中指定的局部变量值
  • 10. 当一个实例方法被调用的时候,它的方法参数和方法体内部定义的局部变量将会按照顺序被复制到局部变量表中的每一个slot上
  • 11. 如果需要访问局部变量表中一个64bit的局部变量值时,只需要使用前一个索引即可。比如访问long或double类型变量
  • 12. 如果当前帧是由构造方法或者实例方法创建的,该对象引用this将会存放在index为0的slot处,其余的参数按照参数表顺序继续排列
  • 13. 静态方法中不能用this,是因为静态方法的局部变量表中没有this变量。构造方法和类实例方法的本地变量表index为0的slot处会存放this变量
3.2 slot索引图

【jvm】虚拟机栈之局部变量表_第5张图片

3.3 实例方法的局部变量表

【jvm】虚拟机栈之局部变量表_第6张图片

3.4 long和double类型变量占2个slot

【jvm】虚拟机栈之局部变量表_第7张图片

四、slot的重复利用
4.1 说明
  • 1.栈帧中的局部变量表中的槽位是可以重复利用的
  • 2.如果一个局部变量过了其作用域,在其作用域之后申明的新的局部变量有可能会复用过期的局部变量的槽位,达到节省资源空间的目的
4.2 变量c复用变量b的槽位

【jvm】虚拟机栈之局部变量表_第8张图片

五、静态变量与局部变量对比
  • 1. 参数表分配完毕之后,再根据方法体内定义的变量的顺序和作用域分配
  • 2. 类变量表有两次初始化的机会,第一次是在准备阶段,执行系统初始化,对类变量设置零值,第二次是在初始化阶段,赋予程序员在代码中定义的初始值
  • 3. 局部变量表不存在系统初始化的过程,一旦定义了局部变量则必须程序员来初始化,否则无法使用
  • 4.下面代码是错误的,没有赋值不能使用
    【jvm】虚拟机栈之局部变量表_第9张图片
  • 5. 成员变量在使用前,都经历过默认初始化赋值
  • 6. 成员变量中的类变量在linking的prepare阶段给类变量默认赋值,initial阶段给类变量显示赋值即静态代码块赋值
  • 7. 成员变量中的实例变量随着对象
  • 8. 局部变量在使用前必须要进行显示赋值,否则编译不通过

你可能感兴趣的:(jvm,jvm)