睁大眼睛瞧过去,感觉内容挺多的。同学们不要着急,我们来一行一行分析。
第 1 行:
Classfile /Users/maweiqing/Documents/GitHub/TechSisterLearnJava/codes/TechSister/target/classes/com/itwanger/jvm/Main.class
字节码文件的位置。
第 2 行:
Last modified 2021年4月15日; size 385 bytes
字节码文件的修改日期、文件大小。
第 3 行:
SHA-256 checksum 6688843e4f70ae8d83040dc7c8e2dd3694bf10ba7c518a6ea9b88b318a8967c
字节码文件的 SHA-256 值。
第 4 行:
Compiled from "Main.java"
说明该字节码文件编译自 Main.java 源文件。
第 5 行:
public class com.itwanger.jvm.Main
字节码文件的类全名。
第 6 行 minor version: 0,次版本号。
第 7 行 major version: 55,主版本号。
第 8 行:
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
类访问标记,一共有 8 种。
表明当前类是 ACC_PUBLIC | ACC_SUPER。位运算符 | 的意思是如果相对应位是 0,则结果为 0,否则为 1,所以 0x0001 | 0x0020 的结果是 0x0021(需要转成二进制进行运算)。
第 9 行:
this_class: #3 // com/itwanger/jvm/Main
当前类的索引,指向常量池中下标为 3 的常量,可以看得出当前类是 Main 类。
第 10 行:
super_class: #4 // java/lang/Object
父类的索引,指向常量池中下标为 6 的常量,可以看得出当前类的父类是 Object 类。
第 11 行:
interfaces: 0, fields: 1, methods: 2, attributes: 1
当前类有 0 个接口,1 个字段(age),2 个方法(write()方法和缺省的默认构造方法),1 个属性(该类仅有的一个属性是 SourceFIle,包含了源码文件的信息)。
03、常量池
接下来是 Constant pool,也就是字节码文件最重要的常量池部分。可以把常量池理解为字节码文件中的资源仓库,主要存放两大类信息。
1)字面量(Literal),有点类似 Java 中的常量概念,比如文本字符串,final 常量等。
2)符号引用(Symbolic References),属于编译原理方面的概念,包括 3 种:
类和接口的全限定名(Fully Qualified Name)
字段的名称和描述符(Descriptor)
方法的名称和描述符
Java 虚拟机是在加载字节码文件的时候才进行的动态链接,也就是说,字段和方法的符号引用只有经过运行期转换后才能获得真正的内存地址。当 Java 虚拟机运行时,需要从常量池获取对应的符号引用,然后在类创建或者运行时解析并翻译到具体的内存地址上。
当前字节码文件中一共有 21 个常量,它们之间是有链接的,逐个分析会比较乱,我们采用顺藤摸瓜的方式,从上依次往下看,那些被链接的常量我们就点到为止。
注:
# 号后面跟的是索引,索引没有从 0 开始而是从 1 开始,是因为设计者考虑到,“如果要表达不引用任何一个常量的含义时,可以将索引值设为 0 来表示”(《深入理解 Java 虚拟机》描述的)。
= 号后面跟的是常量的类型,没有包含前缀 CONSTANT_ 和后缀 _info。
全文中提到的索引等同于下标,为了灵活描述,没有做统一。
第 1 个常量:
#1 = Methodref #4.#18 // java/lang/Object."<init>":()V
类型为 Methodref,表明是用来定义方法的,指向常量池中下标为 4 和 18 的常量。
第 4 个常量:
#4 = Class #21 // java/lang/Object
类型为 Class,表明是用来定义类(或者接口)的,指向常量池中下标为 21 的常量。
第 21 个常量:
#21 = Utf8 java/lang/Object
类型为 Utf8,UTF-8 编码的字符串,值为 java/lang/Object。
第 18 个常量:
#18 = NameAndType #7:#8 // "<init>":()V
类型为 NameAndType,表明是字段或者方法的部分符号引用,指向常量池中下标为 7 和 8 的常量。
第 7 个常量:
#7 = Utf8 <init>
类型为 Utf8,UTF-8 编码的字符串,值为 <init>,表明为构造方法。
第 8 个常量:
#8 = Utf8 ()V
类型为 Utf8,UTF-8 编码的字符串,值为 ()V,表明方法的返回值为 void。
到此为止,第 1 个常量算是摸完了。组合起来的意思就是,Main 类使用的是默认的构造方法,来源于 Object 类。
第 2 个常量:
#2 = Fieldref #3.#19 // com/itwanger/jvm/Main.age:I
类型为 Fieldref,表明是用来定义字段的,指向常量池中下标为 3 和 19 的常量。
第 3 个常量:
#3 = Class #20 // com/itwanger/jvm/Main
类型为 Class,表明是用来定义类(或者接口)的,指向常量池中下标为 20 的常量。
第 19 个常量:
#19 = NameAndType #5:#6 // age:I
类型为 NameAndType,表明是字段或者方法的部分符号引用,指向常量池中下标为 5 和 6 的常量。
第 5 个常量:
#5 = Utf8 age
类型为 Utf8,UTF-8 编码的字符串,值为 age,表明字段名为 age。
第 6 个常量:
#6 = Utf8 I
类型为 Utf8,UTF-8 编码的字符串,值为 I,表明字段的类型为 int。
关于字段类型的描述符映射表如下图所示。