JVM003_属性表

属性表

预备知识

  • javac -g Xxx.java 在生成class文件的时候生成所有调试信息
  • javap -v Xxx.class 输出附加信息

属性表结构

类型名称数量备注
u2attribute_name_index1属性名称索引,指向一个CONSTANT_Utf8_info型常量的索引
u4attribute_length1该属性表的长度
u1infoattribute_length属性值

Code属性表

在这里插入图片描述

Code属性表结构释义

  • attribute_name_index是一项指向CONSTANT_Utf8_info型常量的索引,对应的值固定为‘Code’,代表了该属性的属性名称。

  • attribute_length代表了属性值的长度。属性值的长度=属性表总长度-6个字节。

  • max_stack代表操作数栈深度的最大值。虚拟机运行时根据该值来分配栈帧中操作数栈的深度。

  • max_locals 代表了局部变量表所需的存储空间。其计算单位为‘变量槽’。变量槽是JVM为局部变量表分配内存所使用的最小单位。对于byte、char、float、int、short、boolean及returnAddress等长度不超过32位的数据类型,每个局部变量占用1个变量槽,double及long这两种64位的数据类型,占用两个变量槽。

    • 局部变量表中的变量槽会被复用,所以变量槽的个数不一定等于参数个数。当代码执行 超过一个变量的作用域时,其原型占用的变量槽将会被复用。
    • 操作数栈及局部变量表直接决定了该方法的栈帧所耗费的内存。
  • code_length代表Java源程序编译后生成的字节码指令个数,由于一个字节码占用1字节,所以也就是字节码的长度。

    • 理论值为2^32,实际最大值为65535。
  • code 用于存储编译生产的一些列字节码指令。

  • exception_info 该方法的显示异常处理表集合,非必须。

    • 显示异常处理表结构

      类型名称数量
      u2start_pc1
      u2end_pc1
      u2handler_pc1
      u2catch_type1

      上表表示当字节码从[start_pc,end_pc)行出现了类型为catch_type或其子类的异常,那么就转至handler_pc行进行处理。当catch_type为0时,代表任意异常都需要跳转到handler_pc行处理。

      -注意 : 此处的行指的是字节码相对于方法体开始的偏移量。

Code属性表实例

public class Test{
    private static final int size = 100;

    public void sout(){
        System.out.println(size);
    }

    public int toDouble(int prame){
        return 2*prame;
    }

    public int add(int p1,int p2){
        return p1+p2;
    }

    public static int toTriple(int prame){
    	return 3*prame;
    }
}

将代码编译 javac -g Test.java,在反编译javap -v Test.class

Classfile /D:/notes/JVM/code/Test.class
  Last modified 2021-3-29; size 746 bytes
  MD5 checksum a1a23cf998350d444d9438782f3af659
  Compiled from "Test.java"
public class Test
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#28         // java/lang/Object."<init>":()V
   #2 = Fieldref           #29.#30        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Class              #31            // Test
   #4 = Methodref          #32.#33        // java/io/PrintStream.println:(I)V
   #5 = Class              #34            // java/lang/Object
   #6 = Utf8               size
   #7 = Utf8               I
   #8 = Utf8               ConstantValue
   #9 = Integer            100
  #10 = Utf8               <init>
  #11 = Utf8               ()V
  #12 = Utf8               Code
  #13 = Utf8               LineNumberTable
  #14 = Utf8               LocalVariableTable
  #15 = Utf8               this
  #16 = Utf8               LTest;
  #17 = Utf8               sout
  #18 = Utf8               toDouble
  #19 = Utf8               (I)I
  #20 = Utf8               prame
  #21 = Utf8               add
  #22 = Utf8               (II)I
  #23 = Utf8               p1
  #24 = Utf8               p2
  #25 = Utf8               toTriple
  #26 = Utf8               SourceFile
  #27 = Utf8               Test.java
  #28 = NameAndType        #10:#11        // "<init>":()V
  #29 = Class              #35            // java/lang/System
  #30 = NameAndType        #36:#37        // out:Ljava/io/PrintStream;
  #31 = Utf8               Test
  #32 = Class              #38            // java/io/PrintStream
  #33 = NameAndType        #39:#40        // println:(I)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               (I)V
{
  public Test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LTest;    // 疑问:为什么这里的Length为5?

  public void sout();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: bipush        100
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(I)V
         8: return
      LineNumberTable:
        line 5: 0
        line 6: 8
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   LTest;

  public int toDouble(int);
    descriptor: (I)I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: iconst_2
         1: iload_1
         2: imul
         3: ireturn
      LineNumberTable:
        line 9: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0  this   LTest;
            0       4     1 prame   I

  public int add(int, int);
    descriptor: (II)I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=3
         0: iload_1
         1: iload_2
         2: iadd
         3: ireturn
      LineNumberTable:
        line 13: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0  this   LTest;
            0       4     1    p1   I
            0       4     2    p2   I

  public static int toTriple(int);
    descriptor: (I)I
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: iconst_3
         1: iload_0
         2: imul
         3: ireturn
      LineNumberTable:
        line 17: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       4     0 prame   I
}

现在对toDouble方法进行分析

在这里插入图片描述

Code属性下

  • stack=2表示操作数栈的最大深度为2
  • locals=2表示本地变量表的存储空间为2个变量槽
  • args_size=2表示方法的传参有2个
    • 这里有个问题,明明方法签名为toDouble(int prame),只有一个参数,为什么这里显示有两个参数呢?因为在编译的时候,会自动的传入一个参数this。并放到局部变量表的0号槽中。
  • LocalVariableTable 表示各变量在局部变量表中的存储情况。
Code:
      stack=2, locals=2, args_size=2
         0: iconst_2 # 将int型数值2推送到操作数栈顶
         1: iload_1  # 将第二个int型本地变量推动至栈顶,即将局部变量表中solt索引为1的变量推送至操作数栈栈顶
         2: imul	 # 将栈顶两个int类型数值相乘并将结果压入栈顶
         3: ireturn  # 从方法中返回int类型的数据,即将操作数栈栈顶元素弹出

Exceptions属性

Exceptions属性是在方法表中与Code属性平级的一项属性,与Code属性中的异常表不是同一东西,列举出方法中可能抛出的受查异常(Checked Excepitons) , 也就是方法描述时在throws关键字后面列举的异常。

Exceptions属性结构

类型名称数量备注
u2attribute_name_index1属性名索引
u4attribute_length1属性长度
u2number_of_exceptions1抛出的受查异常种数
u2exception_index_tablenubmer_of_exceptions指向常量池中CONSTANT_class_info型常量的索引

LineNumberTable属性

用来描述Java源码与字节码行号之间的对应关系。这里的字节码行号指的是字节码距方法体开始的偏移量。是一个非必须属性。默认生成。可使用-g: none-g:lines来选择或者取消。

若不生成改属性,那么在抛出异常的时候,堆栈中将不会显示出错的行号,在调试时无法按照源码行来设置断点。

LineNumberTable属性结构

类型名称数量备注
u2attribute_name_index1属性名索引
u4attribute_length1属性长度
u2line_number_table_length1属性个数
line_number_infoline_number_tableline_number_table_lengthline_number_info集合

line_number_info

类型名称备注
u2start_pc字节码行号
u2line_numberJava源码行号

LocalVariableTable

用来描述栈帧中局部变量表的变量与Java源码中定义的变量之间的关系,是非必须的。-g:none,-g: vars来取消或选择。默认会生成到class文件中,若取消该属性,那么当他人引用时,所有的参数名称都会消失。

LocalVariableTable属性结构

类型名称数量备注
u2attribute_name_index1属性名索引
u4attribute_length1属性长度
u2local_variable_table_length1本地变量表长度
local_variable_infolocal_variable_tablelocal_variable_table_length本地变量表

local_variable_info结构

类型名称数量备注
u2start_pc1该局部变量的声明周期的开始字节码偏移量
u2length1该局部变量的作用范围
u2name_index1是指向CONSTANT_Utf8_info型常量的索引,代表局部变量的名称
u2descriptor_index1是指向CONSTANT_Utf8_info型常量的索引,代表局部变量的描述符
u2index1在局部变量表中的变量槽位置

在JDK引入泛型后增加了**LocalVariableTypeTable **属性,仅仅是将descriptor_index替换为字段的特征签名(Signature) 。

SourceFile及SourceDebugExtension属性

SourceFile属性用于记录生成这个Class文件的源码文件名称。 可选;-g:none,-g: source来关闭或开启。若不生成,在抛出异常的时候,堆栈中不会显示出错代码所属的文件名。

SouceFile属性结构

类型名称数量备注
u2attribute_name_index1属性名索引
u4attribute_length1属性长度
u2sourcefile_index1指向常量池中CONSTANT_Utf8_info型常量的索引, 常量值是源码文件的文件名

JDK 5时, 新增了SourceDebugExtension属性用于存储额外的代码调试信息。

类型名称数量备注
u2attribute_name_index1属性名索引
u4attribute_length1属性长度
u1debug_extension[attribute_length]1额外的调试信息

ConstantValue属性

该属性的作用是通知虚拟机自动为静态变量赋值。只有被static修饰的变量才能使用这项属性。

非静态变量实例构造器()方法中赋值
静态变量类构造器()方法中赋值
静态变量使用ConstantValue属性

Oracle公司的选择是,常量(static final共同修饰的变量)且其类型为基础类型或String,使用ConstantValue属性来进行初始化,非基础类型及字符串,或仅被static修饰那么在()中进行初始化。

ConstantValue属性结构

类型名称数量备注
u2attribute_name_index1属性名索引
u4attribute_length1属性长度
u2constantvalue_index1常量池中一个字面量的引用
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值