• JVM 字节码的结构


    编译的.class文件,可以用javap进行反编译

    javap Test.class

    javap -c Test.class

    javap -verbose Test.class

    1、创建MyTest1.java

    public class MyTest1 {
        private int a = 1;
    
        public MyTest1() {
        }
    
        public int getA() {
            return this.a;
        }
    
        public void setA(int a) {
            this.a = a;
        }
    }
    

    使用D:workspacestudy jvm_demouildclassesjavamaincomexamplejvmytecode>javap -verbose  MyTest1.class

    Classfile /D:/workspace/study/ jvm_demo/build/classes/java/main/com/example/jvm/bytecode/MyTest1.class
      Last modified 2019-6-23; size 495 bytes
      MD5 checksum 54c0850cfabb7f115919c93e556e3630
      Compiled from "MyTest1.java"
    public class com.example.jvm.bytecode.MyTest1
      SourceFile: "MyTest1.java"
      minor version: 0
      major version: 51
      flags: ACC_PUBLIC, ACC_SUPER
    Constant pool:
       #1 = Methodref          #4.#20         //  java/lang/Object."<init>":()V
       #2 = Fieldref           #3.#21         //  com/example/jvm/bytecode/MyTest1.a:I
       #3 = Class              #22            //  com/example/jvm/bytecode/MyTest1
       #4 = Class              #23            //  java/lang/Object
       #5 = Utf8               a
       #6 = Utf8               I
       #7 = Utf8               <init>
       #8 = Utf8               ()V
       #9 = Utf8               Code
      #10 = Utf8               LineNumberTable
      #11 = Utf8               LocalVariableTable
      #12 = Utf8               this
      #13 = Utf8               Lcom/example/jvm/bytecode/MyTest1;
      #14 = Utf8               getA
      #15 = Utf8               ()I
      #16 = Utf8               setA
      #17 = Utf8               (I)V
      #18 = Utf8               SourceFile
      #19 = Utf8               MyTest1.java
      #20 = NameAndType        #7:#8          //  "<init>":()V
      #21 = NameAndType        #5:#6          //  a:I
      #22 = Utf8               com/example/jvm/bytecode/MyTest1
      #23 = Utf8               java/lang/Object
    {
      public com.example.jvm.bytecode.MyTest1();
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: aload_0
             5: iconst_1
             6: putfield      #2                  // Field a:I
             9: return
          LineNumberTable:
            line 6: 0
            line 8: 4
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                   0      10     0  this   Lcom/example/jvm/bytecode/MyTest1;
    
      public int getA();
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: getfield      #2                  // Field a:I
             4: ireturn
          LineNumberTable:
            line 12: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                   0       5     0  this   Lcom/example/jvm/bytecode/MyTest1;
    
      public void setA(int);
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=2, args_size=2
             0: aload_0
             1: iload_1
             2: putfield      #2                  // Field a:I
             5: return
          LineNumberTable:
            line 16: 0
            line 17: 5
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                   0       6     0  this   Lcom/example/jvm/bytecode/MyTest1;
                   0       6     1     a   I
    }
    

      

    2、使用WinHex打开MyTest1.class文件

    1、使用javap -verbose命令分析一个字节码文件时,将会分析该字节码文件的魔数、版本号、常量池、类的构造方法、类中的方法信息、类变量与成员变量等信息。
    2、魔数: 所有的.class字节码文件的前4个字节都是魔数,魔数值为固定值: 0xCAFEBABE.
    3、魔数之后的四个字节为版本信息,前两个字节表示minor version(次版本号),后两个字节表示major version(主版本号)。这里的版本号为 00 00 00 33,换算成十进制,表示次版本为0,主版本为51. (51代表JDK7). 所有该文件的版本号为: 1.7.0
    4、常量池(constant pool): 紧接着主版本号之后的就是常量池入口。一个Java类中定义的很多信息都是由常量池来维护和描述的,可以将常量池看作是Class文件的资源仓库,比如说Java类中定义的方法与变量信息,都是存储在常量池中。常量池主要存储两类常量: 字面量与符号引用。字面量如文本字符串,Java中声明为final的 常量值等,而符号引用如类和接口的全局限定名,字段的名称和描述符,方法的名称和描述符等。
    5、常量池的总体结构: Java类所对应常量池主要由常量池数量与常量池数组(常量表)这两部分共同构成。常量池数量紧跟在主版本号后面,占据2个字节; 常量池数组则紧跟在常量池数量之后。常量池数组与一般的数组不同的是,常量池数组中不同的元素的类型、结构都是不同的,长度当然也是不同; 但是,每一种元素的第一个数据都是一个u1类型,该字节是标志位,占据1个字节。JVM在解析常量池时,会根据这个u1类型来获取元素的具体类型。值得注意的是,常量池数组中元素的个数 = 常量池数 - 1 (其中0暂时不使用), 目的是满足某些常量池索引值的数据在特定情况下需要表单[不引用任何一个常量池]的含义 ; 根本原因在于,索引为0也是一个常量(保留常量),只不过它不位于常量表中,这个常量就对应null值; 所以常量池的索引从1而非0开始。
    6、在JVM规范中,每个变量/字段都有描述信息,描述信息主要的作用是描述字段的数据类型、方法的参数列表(包括数量、类型与顺序)与返回值。根据描述规则,基本数据类型和代表无返回值的void类型都用一个大写字符来表示,对象类型则使用字符L加对象的全限定名称来表示。为了压缩字节码文件的体积,对于基本数据类型,JVM都只使用一个大写字母来表示,如下所示B - byte, C - char, D - double, F - float, I - int, J - long, S - short, Z -boolean, V -void, L -对象类型, 如Ljava/lang/String;
    7、对于数组来说,每一个维度使用一个前置的[来表示,如int[]被记录为[I, String[][]被记录为[[Ljava/lang/String
    8、用描述符描述方法时,按照先参数列表,后返回值的顺序来描述。参数列表按照参数的严格顺序放在一组()之内,如方法String getRealnamebyIdAndNickname(int id, String name)的描述符为:(I,Ljava/lang/String;) Ljava/lang/String;

    3、Class文件结构中常量池中11种数据类型的结构总表

     上面的表中描述了11中数据类型的结构,其实在jdk1.7之后又增加了3种

    CONSTANT_MethodHandle_info, CONSTANT_MethodType_info 以及CONSTANT_InvokeDynamic_info) 这样总共14种。

    4、Java字节码整体结构

    Access_Flag访问标志: 访问标志信息包括该Class文件是类还是接口,是否被定义成public,是否是abstract,如果是类,是否被声明成final。通过上面的MyTest1源代码,我们知道该文件是类并且是public。 

    0x 00 21: 是0x 0020 和 0x0001的并集,表示ACC_PUBLIC与ACC_SUPER

    Fields字段表集合: 字段表用于描述类和接口中声明的变量。这里的字段包含了类级别变量(静态变量)以及实例变量(成员变量),但是不包含方法内声明的局部变量

     字段表结构

     field_info

    5、Java字节码整体结构

    在Class文件的定义

    6、Class字节码中有两种数据类型

    1)字节数据直接量: 这是基本的数据类型。共细分为u1,u2,u4,u8四种,分别代表连续的1个字节,2个字节,4个字节,8个字节组成的整体数据。

    2)表(数组): 表是由多个基本数据或其它表,按照既定顺序组成的大的数据集合。表是有结构的,它的结构体现在: 组成表的成分所在的位置和顺序都是已经严格定义好的。

  • 相关阅读:
    第三周助教总结
    第三周作业
    第二周助教总结
    参数和指针
    第二周作业
    第一周作业 2
    第一周作业 1
    第七周助教小结
    第六周助教小结
    第五周助教总结
  • 原文地址:https://www.cnblogs.com/linlf03/p/11074069.html
Copyright © 2020-2023  润新知