Class字节码整体结构 常量池 字段表方法表详解

目录

  • class字节码
    • 示例
    • 字节码整体结构
    • 顺序从上到下
      • 魔数(Magic Number)
      • 版本(Version)
      • 常量池(Constant Pool)
      • 例如
        • 总结
      • Mytest1常量池对应的内容
      • 访问标志(Access Flags)
      • 当前Class名(This Class Name )
      • 父类名(super Class)
      • 接口(Interface)
      • 变量(Feilds)
        • field count
        • fields
      • 方法(Methods)
        • 方法表(方法数组)
      • Code结构

class字节码

示例

public class MyTest1 {
     

    private int a = 1;

    public int getA() {
     
        return a;
    }

    public void setA(int a) {
     
        this.a = a;
    }
}

执行

javac MyTest1.java
javap -verbose MyTest1.class

得到对应字节码

  Last modified 2020-9-13; size 375 bytes
  MD5 checksum 8ba81fc79a80d987e02b14067a155703
  Compiled from "MyTest1.java"
public class com.example.demo.com.jvm.bytecode.MyTest1
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #4.#17         // java/lang/Object."":()V
   #2 = Fieldref           #3.#18         //com/example/demo/com/jvm/bytecode/MyTest1.a:I
   #3 = Class              #19            // com/example/demo/com/jvm/bytecode/MyTest1
   #4 = Class              #20            // java/lang/Object
   #5 = Utf8               a
   #6 = Utf8               I
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               getA
  #12 = Utf8               ()I
  #13 = Utf8               setA
  #14 = Utf8               (I)V
  #15 = Utf8               SourceFile
  #16 = Utf8               MyTest1.java
  #17 = NameAndType        #7:#8          // "":()V
  #18 = NameAndType        #5:#6          // a:I
  #19 = Utf8               com/example/demo/com/jvm/bytecode/MyTest1
  #20 = Utf8               java/lang/Object
{
     
  public com.example.demo.com.jvm.bytecode.MyTest1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."
":()V
         4: aload_0
         5: iconst_1
         6: putfield      #2                  // Field a:I
         9: return
      LineNumberTable:
        line 3: 0
        line 5: 4

  public int getA();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #2                  // Field a:I
         4: ireturn
      LineNumberTable:
        line 8: 0

  public void setA(int);
    descriptor: (I)V
    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 12: 0
        line 13: 5
}
SourceFile: "MyTest1.java"

字节码整体结构

Class字节码整体结构 常量池 字段表方法表详解_第1张图片
这里面表就类似常量池 是一种数据结构的复合形式

Class字节码整体结构 常量池 字段表方法表详解_第2张图片
Class字节码整体结构 常量池 字段表方法表详解_第3张图片
Class字节码整体结构 常量池 字段表方法表详解_第4张图片

顺序从上到下

  1. 使用javap -verbose命令分析一个字节码文件时,将会分析该字节码文件的魔数,版本号,常量池,类信息,类的构造方法,类中的方法信息,类变量与成员变量信息。

  2. 魔数:所有的.class字节码文件的前四个字节都是魔数,魔数值为固定值:0xCAFFBABE【咖啡宝贝】(在解析时会查找固定魔数当不符合规范就不进行解析了)

  3. 3.魔数之后的4个字节为版本信息,前两个字节表示minnor version(次版本号),后两个字节表示major version(主版本号)。这几的版本号为00 00 00 34,换算成十进制,表示次版本号为0,主版本号为52.所以改文件的版本号为:1.8.0 可以通过java -version命令来验证这一点。

  4. 常量池(constant pool):紧接着主版本号之后的就是常量池入口。一个java类中定义的很多信息都是由常量池来维护和描述的,可以将常量池看作是Class文件的资源仓库,比如说java类中定义的方法与变量信息,都是存储在常量池当中。常量池中主要存储两类常量:字面量和符号引用。字面量如文本字符串,java声明的final的常量值等,而符号引用如类和接口的全局限定名。字段的名称和描述符符,方法的名称和描述符等。

  5. 常量池的总体结构:Java类所对应的常量池主要由常量池数量和常量池数组(常量表)这两部分共同构成。常量池数量紧跟在主版本号后面,占据2个字节;常量池数组则紧跟在常量池数量之后,常量池数组与一般的数组不同的是,常量池数组中不同的元素的类型,结构都是不同的,长度当然也就不同;但是每一种元素的第一个数据都是一个u1类型,该字节是个标志位,占据1个字节。JVM在解析常量池时,会根据这个u1类型来获取元素的具体类型。值得注意的是,常量池数组中元素的个数 = 常量池Count - 1(其中0暂时不使用),目的是满足某些常量池索引值的数据在特定请款项需要表达【不引用任何一个常量池】的含义;根本原因在于,索引为0也是一个常量(保留常量),只不过它位于常量表中,这个常量就对应null值;所以,常量池的索引从1而非0开始。

  6. 在JVM规范中,每个变量/字段都有描述信息,描述信息主要的作用是描述字段的数据类型。方法的参数列表(包括数量,类型和顺序)与返回值。根据描述符规则,基本数据类型和代表无返回值的void类型都用一个大写的字符来表示,对象类型则使用字符L加对象的全限定名称来表示。为了压缩字节码文件的体积,对于基本数据类型,JVM都只使用一个大写的字母来表示,如下所示:B - byte,C - char, D - double, F- float,I - int, J - lang, 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;

魔数(Magic Number)

0xCAFFBABE【咖啡宝贝】(在解析时会查找固定魔数当不符合规范就不进行解析了)

版本(Version)

Class字节码整体结构 常量池 字段表方法表详解_第5张图片

常量池(Constant Pool)

分为两部分版本后两个字节是对应的常量count 第二部分是常量表(资源仓库)为count-1个常量
编译之后的字节码文件
Class字节码整体结构 常量池 字段表方法表详解_第6张图片

Class字节码整体结构 常量池 字段表方法表详解_第7张图片
Class字节码整体结构 常量池 字段表方法表详解_第8张图片

例如

字节码常量池对应的第一个参数 0A(固定都是第一个字节)对应的是10如图
在这里插入图片描述
此时后续4(2+2)个字节都是描述 对应的index 在这里插入图片描述
4 = 416^0 指向如图下 #4 = Class
20 = 1
16^1 +4*16 #20 = NameAndType
而 #4 指向 #23 java/lang/Object
#20 指向 #7#8 分别为 对应构造方法, ()V 对应构造方法无参无返回
组合就是java/lang/Object."":()V 对应的就是
第二个常量池信息如下图
在这里插入图片描述
对应的 com/cx/jvm/bytecode/Mytest1.a:I 对应的意思是: myTest1类 参数 int类型a属性

在这里插入图片描述
对应表格表示的是 为长度为1个字节的字符串 61 对应的 2进制为 97 97是a的ASCII码值 其他如下(我们可以在线转ASCII值)
如01 00 06 3c 69 6e 69 74 3e
Class字节码整体结构 常量池 字段表方法表详解_第9张图片

总结

所以 jvm通过压缩将其class文件 字节码所有字节通过jvm规范通过ASCII都能转化成对应的java文件 可以看出方法构造方法和属性 父类信息都是存储在常量池的

Mytest1常量池对应的内容

Class字节码整体结构 常量池 字段表方法表详解_第10张图片
jvm在加载的时候已经知道到需要解析的常量池对应的常量数据(都是通过他们常量的标识符来确定的) 如上 当解析到23个时就解析完成。

访问标志(Access Flags)

Class字节码整体结构 常量池 字段表方法表详解_第11张图片
对应类的访问修饰符对应如下几种
Class字节码整体结构 常量池 字段表方法表详解_第12张图片
ACC_PRIVATE 0X0002 表示private修饰符
当存在多种类修饰符 相当于对应类型值累加
在这里插入图片描述
对应的就是 20+1 = 21
Class字节码整体结构 常量池 字段表方法表详解_第13张图片
Class字节码整体结构 常量池 字段表方法表详解_第14张图片

当前Class名(This Class Name )

#
此时会去常量池(资源仓库)去找#3对应的数据 当前例子#3指向#19对应的就是com/example/demo/com/jvm/bytecode/MyTest1完全的限定名

 #19 = Utf8               com/example/demo/com/jvm/bytecode/MyTest1

父类名(super Class)

Class字节码整体结构 常量池 字段表方法表详解_第15张图片
#4 = Class #20 // java/lang/Object 都是去常量池中找对应的数据

接口(Interface)

接口分为两部分 接口数量(super class后两个字节)以及接口表
当接口count为0 接口表就不会出现

变量(Feilds)

Class字节码整体结构 常量池 字段表方法表详解_第16张图片

变量分为两部分变量数量(interface后两个字节 只统计类中静态变量和成员变量)以及变量表

field count

在这里插入图片描述

fields

变量表也有自己的结构的
Class字节码整体结构 常量池 字段表方法表详解_第17张图片
access_flags 需要再修饰符中找对应的数据
Class字节码整体结构 常量池 字段表方法表详解_第18张图片
Class字节码整体结构 常量池 字段表方法表详解_第19张图片
此时0006 后面的两个字节是0000无特殊的attributes(如上图字段结构所示) count 就忽略不展示
就可以得出成员变量为 private int a;

方法(Methods)

方法也分为两种 count (两个字节)和方法表
 此离有三个方法 一个无参构造方法和对应get set方法

方法表(方法数组)

结构
Class字节码整体结构 常量池 字段表方法表详解_第20张图片
如第一个 方法
Class字节码整体结构 常量池 字段表方法表详解_第21张图片

对应的16进制字符
在这里插入图片描述

attribute_info结构 如下
Class字节码整体结构 常量池 字段表方法表详解_第22张图片
Class字节码整体结构 常量池 字段表方法表详解_第23张图片

Code结构

Class字节码整体结构 常量池 字段表方法表详解_第24张图片
Class字节码整体结构 常量池 字段表方法表详解_第25张图片
Class字节码整体结构 常量池 字段表方法表详解_第26张图片
Class字节码整体结构 常量池 字段表方法表详解_第27张图片
Class字节码整体结构 常量池 字段表方法表详解_第28张图片
Class字节码整体结构 常量池 字段表方法表详解_第29张图片

你可能感兴趣的:(JVM,数据结构,java,jvm,class,jdk)