跳至主要內容

③JVM 类文件结构

holic-x...大约 9 分钟JAVA基础

③JVM 类文件结构

学习核心

  • 类文件结构(了解)

学习资料

核心概念

​ 无关性的基础:虚拟机+字节码

平台无关性:Java从诞生之初就有一个口号“一次编写,到处运行”,也就是说你编写的Java程序不用考虑是要运行到什么类型的操作系统上,都是可以运行的

语言无关性:Java虚拟机(JVM)只能执行Java代码编写的程序码?不是的,Java虚拟机只关心字节码文件,程序的运行是虚拟机解释执行字节码文件来完成的。至于字节码文件怎么生成的,虚拟机就不做限制了。目前能够生成字节码文件的语言有(Java,Jruby,Groovy,Kotlin)等。

image-20240531151641059

Java字节码文件

任何一个Class文件都对应着唯一的一个类或接口的定义信息,但是反过来说,类或接口并不一定都得定义在文件里;

​ Class文件是一组以8个字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在class文件中,这个二进制流可以不一定来自于磁盘文件,也可以来自于网络,或者动态生成。Class文件格式只有两种数据类型,“无符号数”和“表” ​ 无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个 字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。

​ 表是由多个无符号数或者其他表作为数据项构成的复合数据类型,为了便于区分,所有表的命名都习惯性地以info”结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上也可以视作是一张表。

1.Class文件的结构属性

B(魔数与Class文件版本)

​ 每个Class文件的头4个字节被称为魔数(Magic Number),Class文件的魔数取得很有“浪漫气息”,"值为0xCAFEBABE(咖 啡 宝 贝?)。它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件

​ 紧接着魔数的4个字节存储的是Class文件的版本号;第5和第6个字节是次版本号

​ 高版本JDK可以执行低版本文件,但是低版本JDK不能执行高版本文件

C(常量池)

​ 紧接着主、次版本号之后的是常量池入口。常量池是class文件中关联最多的数据类型,也是占用class文件最大的数据项之一,也是第一个出现的表类型数据项目

​ 常量池中主要存放两类常量:

  • 字面量:文本字符串、final常量值
  • 符号引用:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符、方法句柄和方法类型、动态调用点和动态常量等

D(访问标志)

​ 访问标志在常量池结束之后,紧接着的2个字节代表访问标志

​ 访问标志主要描述这个Class是类还是接口、是否定义为public类型、是否定义为abstract 类型

​ 如果是类的话,是否被声明为final,是否为注解,是否为枚举值等等

E(类索引,父类索引,接口索引)

​ 主要确定该类型的继承关系,即继承了哪些父类,实现了哪些接口等。类索引和父类索引都是一个u2数据类型而接口索引是一个u2类型的数据的集合(一个类可以继承一个父类,但可以实现多个接口)

F(字段表集合)

​ 字段表(field info)用于描述接口或者类中声明的变量。“字段”(Field)包括类级变量以及实例级变量,但不包括在方法内部声明的局部变量

​ 联想一个类中字段的定义信息,主要包括:作用域(public,private..),受否static,是否final,数据类型(基本类型,对象,数组)等

G(方法表集合)

​ 参考字段的定义,与之类似。也包括作用域,是否static,是否Synchronized等

H(属性表集合)

​ 属性表(attribute info),Class文件、字段表、方法表都可以携带自己的属性表集合,以描述某些场景专有的信息

2.示例(分析Java类源码和字节码文件)

Student类定义

public class Student {

    private static final int AGE = 10;

    private String id;
    private String name;
    private int age;

    Student(String id, String name, int age) {
        id = "id_0001";
        name = "noob";
        age = AGE;
    }

    public String getStu() {
        return id + ":" + name + ":" + age;
    }
}

编译+反编译查看字节码文件

# 编译生成class文件
javac Student.java

# 反编译查看class文件(指定-verbose选项会输出附加信息)
javap -verbose -p Student.class 
javap -v -p Student.class 

​ 字节码文件如下所示,结合上述结构概念拆解字节码文件信息

Classfile /Users/holic-x/workspace/git-repo/github/study-project/noob-demo/rebirth/base/JavaBase/src/main/java/com/noob/jvm/Student.class
  Last modified 2024年5月31日; size 990 bytes
  SHA-256 checksum 2119251c90fb883104a8b8cca52229ede14aad13f8b4f40ed563b69f017dc5ec
  Compiled from "Student.java"
public class com.noob.jvm.Student
  minor version: 0
  major version: 61
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #11                         // com/noob/jvm/Student
  super_class: #2                         // java/lang/Object
  interfaces: 0, fields: 4, methods: 2, attributes: 3
Constant pool:
   #1 = Methodref          #2.#3          // java/lang/Object."<init>":()V
   #2 = Class              #4             // java/lang/Object
   #3 = NameAndType        #5:#6          // "<init>":()V
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = String             #8             // id_0001
   #8 = Utf8               id_0001
   #9 = String             #10            // noob
  #10 = Utf8               noob
  #11 = Class              #12            // com/noob/jvm/Student
  #12 = Utf8               com/noob/jvm/Student
  #13 = Fieldref           #11.#14        // com/noob/jvm/Student.id:Ljava/lang/String;
  #14 = NameAndType        #15:#16        // id:Ljava/lang/String;
  #15 = Utf8               id
  #16 = Utf8               Ljava/lang/String;
  #17 = Fieldref           #11.#18        // com/noob/jvm/Student.name:Ljava/lang/String;
  #18 = NameAndType        #19:#16        // name:Ljava/lang/String;
  #19 = Utf8               name
  #20 = Fieldref           #11.#21        // com/noob/jvm/Student.age:I
  #21 = NameAndType        #22:#23        // age:I
  #22 = Utf8               age
  #23 = Utf8               I
  #24 = InvokeDynamic      #0:#25         // #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;
  #25 = NameAndType        #26:#27        // makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;
  #26 = Utf8               makeConcatWithConstants
  #27 = Utf8               (Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;
  #28 = Utf8               AGE
  #29 = Utf8               ConstantValue
  #30 = Integer            10
  #31 = Utf8               (Ljava/lang/String;Ljava/lang/String;I)V
  #32 = Utf8               Code
  #33 = Utf8               LineNumberTable
  #34 = Utf8               getStu
  #35 = Utf8               ()Ljava/lang/String;
  #36 = Utf8               SourceFile
  #37 = Utf8               Student.java
  #38 = Utf8               BootstrapMethods
  #39 = MethodHandle       6:#40          // REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #40 = Methodref          #41.#42        // java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #41 = Class              #43            // java/lang/invoke/StringConcatFactory
  #42 = NameAndType        #26:#44        // makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #43 = Utf8               java/lang/invoke/StringConcatFactory
  #44 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #45 = String             #46            // \u0001:\u0001:\u0001
  #46 = Utf8               \u0001:\u0001:\u0001
  #47 = Utf8               InnerClasses
  #48 = Class              #49            // java/lang/invoke/MethodHandles$Lookup
  #49 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #50 = Class              #51            // java/lang/invoke/MethodHandles
  #51 = Utf8               java/lang/invoke/MethodHandles
  #52 = Utf8               Lookup
{
  private static final int AGE;
    descriptor: I
    flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL
    ConstantValue: int 10

  private java.lang.String id;
    descriptor: Ljava/lang/String;
    flags: (0x0002) ACC_PRIVATE

  private java.lang.String name;
    descriptor: Ljava/lang/String;
    flags: (0x0002) ACC_PRIVATE

  private int age;
    descriptor: I
    flags: (0x0002) ACC_PRIVATE

  com.noob.jvm.Student(java.lang.String, java.lang.String, int);
    descriptor: (Ljava/lang/String;Ljava/lang/String;I)V
    flags: (0x0000)
    Code:
      stack=1, locals=4, args_size=4
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: ldc           #7                  // String id_0001
         6: astore_1
         7: ldc           #9                  // String noob
         9: astore_2
        10: bipush        10
        12: istore_3
        13: return
      LineNumberTable:
        line 11: 0
        line 12: 4
        line 13: 7
        line 14: 10
        line 15: 13

  public java.lang.String getStu();
    descriptor: ()Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: getfield      #13                 // Field id:Ljava/lang/String;
         4: aload_0
         5: getfield      #17                 // Field name:Ljava/lang/String;
         8: aload_0
         9: getfield      #20                 // Field age:I
        12: invokedynamic #24,  0             // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;
        17: areturn
      LineNumberTable:
        line 18: 0
}
SourceFile: "Student.java"
BootstrapMethods:
  0: #39 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #45 \u0001:\u0001:\u0001
InnerClasses:
  public static final #52= #48 of #50;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles

class文件解析版本

Classfile /Users/holic-x/workspace/git-repo/github/study-project/noob-demo/rebirth/base/JavaBase/src/main/java/com/noob/jvm/Student.class
  Last modified 2024531; size 990 bytes
  SHA-256 checksum 2119251c90fb883104a8b8cca52229ede14aad13f8b4f40ed563b69f017dc5ec
  Compiled from "Student.java"
public class com.noob.jvm.Student
  minor version: 0      // 文件版本:次版本号
  major version: 61     // 文件版本:主版本号
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER    // 访问标志:是否为public类型
  this_class: #11                          // 类索引:看常量池中的#11、#12
  super_class: #2                          // 父类索引:看常量池中的#2、¥4
  interfaces: 0, fields: 4, methods: 2, attributes: 3     // 0个接口、4个字段、2个方法、3个属性
Constant pool:   // 常量池
   #1 = Methodref          #2.#3          // java/lang/Object."<init>":()V
   #2 = Class              #4             // java/lang/Object
   #3 = NameAndType        #5:#6          // "<init>":()V
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = String             #8             // id_0001
   #8 = Utf8               id_0001
   #9 = String             #10            // noob
  #10 = Utf8               noob
  #11 = Class              #12            // com/noob/jvm/Student
  #12 = Utf8               com/noob/jvm/Student
  #13 = Fieldref           #11.#14        // com/noob/jvm/Student.id:Ljava/lang/String;
  #14 = NameAndType        #15:#16        // id:Ljava/lang/String;
  #15 = Utf8               id
  #16 = Utf8               Ljava/lang/String;
  #17 = Fieldref           #11.#18        // com/noob/jvm/Student.name:Ljava/lang/String;
  #18 = NameAndType        #19:#16        // name:Ljava/lang/String;
  #19 = Utf8               name
  #20 = Fieldref           #11.#21        // com/noob/jvm/Student.age:I
  #21 = NameAndType        #22:#23        // age:I
  #22 = Utf8               age
  #23 = Utf8               I
  #24 = InvokeDynamic      #0:#25         // #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;
  #25 = NameAndType        #26:#27        // makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;
  #26 = Utf8               makeConcatWithConstants
  #27 = Utf8               (Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;
  #28 = Utf8               AGE
  #29 = Utf8               ConstantValue
  #30 = Integer            10
  #31 = Utf8               (Ljava/lang/String;Ljava/lang/String;I)V
  #32 = Utf8               Code
  #33 = Utf8               LineNumberTable
  #34 = Utf8               getStu
  #35 = Utf8               ()Ljava/lang/String;
  #36 = Utf8               SourceFile
  #37 = Utf8               Student.java
  #38 = Utf8               BootstrapMethods
  #39 = MethodHandle       6:#40          // REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #40 = Methodref          #41.#42        // java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #41 = Class              #43            // java/lang/invoke/StringConcatFactory
  #42 = NameAndType        #26:#44        // makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #43 = Utf8               java/lang/invoke/StringConcatFactory
  #44 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #45 = String             #46            // \u0001:\u0001:\u0001
  #46 = Utf8               \u0001:\u0001:\u0001
  #47 = Utf8               InnerClasses
  #48 = Class              #49            // java/lang/invoke/MethodHandles$Lookup
  #49 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #50 = Class              #51            // java/lang/invoke/MethodHandles
  #51 = Utf8               java/lang/invoke/MethodHandles
  #52 = Utf8               Lookup
{  // 字段表属性
  private static final int AGE;
    descriptor: I
    flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL
    ConstantValue: int 10

  private java.lang.String id;
    descriptor: Ljava/lang/String;
    flags: (0x0002) ACC_PRIVATE

  private java.lang.String name;
    descriptor: Ljava/lang/String;
    flags: (0x0002) ACC_PRIVATE

  private int age;
    descriptor: I
    flags: (0x0002) ACC_PRIVATE

  // 方法表属性
  com.noob.jvm.Student(java.lang.String, java.lang.String, int);
    descriptor: (Ljava/lang/String;Ljava/lang/String;I)V
    flags: (0x0000)
    Code:
      stack=1, locals=4, args_size=4
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: ldc           #7                  // String id_0001
         6: astore_1
         7: ldc           #9                  // String noob
         9: astore_2
        10: bipush        10
        12: istore_3
        13: return
      LineNumberTable:
        line 11: 0
        line 12: 4
        line 13: 7
        line 14: 10
        line 15: 13

  public java.lang.String getStu();
    descriptor: ()Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: getfield      #13                 // Field id:Ljava/lang/String;
         4: aload_0
         5: getfield      #17                 // Field name:Ljava/lang/String;
         8: aload_0
         9: getfield      #20                 // Field age:I
        12: invokedynamic #24,  0             // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String;
        17: areturn
      LineNumberTable:
        line 18: 0
}
SourceFile: "Student.java"
BootstrapMethods:
  0: #39 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
    Method arguments:
      #45 \u0001:\u0001:\u0001
InnerClasses:
  public static final #52= #48 of #50;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
  • javac编译指令可以指定一些额外的内容输出到字节码:
    • javac -g:lines 强制生成 LineNumberTable
    • javac -g:vars 强制生成 LocalVariableTable
    • javac -g 生成所有的 debug 信息

​ 可以使用jclasslib图形化工具直观地查看字节码内容:jclasslibopen in new window

​ 可以下载UI工具查看,也可借助idea引入插件

image-20240531160331065

image-20240601120111827

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3