2 字节码
主要参考 《Java虚拟机规范(Java SE 8)》。
这部分简单做个了解,有个大概印象。
2.1 class 文件结构总览
虽说是 class 文件,但其实字节码可以存在于任何地方,并不局限于 class 文件。当然,一般为了描述方便,统称为 class 文件。
一个完整的 class 文件包括以下信息:
- magic:魔数,4 字节,固定为 0xCAFEBABE,咖啡宝贝,呼应 Java 的咖啡 logo。没啥用,表示这是一个 class 文件。
- minor_version:副版本号,2 字节。
- major_version:主版本号,2 字节。和 minor_version 一起描述了 class 文件的版本号,JVM 可以根据这个版本号判断自己是否接受这个版本。
- constant_pool_count:常量池计数器,2 字节。
- constant_pool:常量池(表),变长。常量池记录信息的方式比较复杂,不同类型的常量有不同的数据结构,后面详述。
- access_flags:访问标志,2 字节。
- this_class:当前类在常量池表中的索引值,2 字节。
- super_class:父类在常量池表中的索引值,2 字节。
- interfaces_count:当前类实现的接口数量,2 字节。
- interfaces:接口表,变长,长度根据实现接口的数量变化。
- fields_count:字段数量,2 字节。
- fields:字段表,类变量、实例变量都在这里,因为字段数量不定,所以也是变长。
- methods_count:方法数量。
- methods:方法表,当前类或接口中声明的所有方法,不含继承来的。
- attributes_count:属性数量。
- attributes:属性表,这里存储了一些扩展属性,会被字段表、方法表引用。
2.2 字节码反汇编
随便写个源文件:
package per.lvjc.jvm;
import java.util.function.Function;
public class ClassFileTest implements Function<Object, ClassFileTest> {
private static volatile ClassFileTest instance = new ClassFileTest(4.4, 3);
private final double d = 2.1;
private int i = 1;
public ClassFileTest(double d, int i) {
this.i = i;
}
@Override
public ClassFileTest apply(Object o) {
if (o == null) {
return null;
}
if (o instanceof ClassFileTest) {
return (ClassFileTest) o;
}
return instance;
}
public static void main(String[] args) {
ClassFileTest classFileTest = new ClassFileTest(1.1, 5);
double x = classFileTest.d + classFileTest.i;
System.out.println(x);
}
}
编译成 class 文件,再执行 javap 命令:
javap -v -p ./target/classes/per/lvjc/jvm/ClassFileTest.class > ClassFileTest_javap.txt
输出了一个 200 多行的字节码反汇编文件。
2.2.1 基本信息
Classfile /D:/projects/personal/jvm/target/classes/per/lvjc/jvm/ClassFileTest.class
Last modified 2021-3-24; size 1398 bytes
MD5 checksum a8f7540c73cef1224dec4d3ccaf28ccc
Compiled from "ClassFileTest.java"
public class per.lvjc.jvm.ClassFileTest extends java.lang.Object implements java.util.function.Function<java.lang.Object, per.lvjc.jvm.ClassFileTest>
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
第一部分是一些基本信息,反汇编之后排版上应该经过了调整。this_class,super_class,access_flags 信息被提到前面来了。
这里,minor version 0,major version 52,也就是 52.0,具体是什么版本可以去官方查阅。
access_flag 有两个:
- ACC_PUBLIC,因为这个类申明了 public;
- ACC_SUPER,这个不用在意,Java 8 之后每个类都会有。
2.2.2 常量池
Constant pool:
#1 = Methodref #17.#50 // java/lang/Object."<init>":()V
#2 = Double 2.1d
#4 = Fieldref #6.#51 // per/lvjc/jvm/ClassFileTest.d:D
#5 = Fieldref #6.#52 // per/lvjc/jvm/ClassFileTest.i:I
#6 = Class #53 // per/lvjc/jvm/ClassFileTest
#7 = Fieldref #6.#54 // per/lvjc/jvm/ClassFileTest.instance:Lper/lvjc/jvm/ClassFileTest;
#8 = Double 1.1d
#10 = Methodref #6.#55 // per/lvjc/jvm/ClassFileTest."<init>":(DI)V
#11 = Methodref #17.#56 // java/lang/Object.getClass:()Ljava/lang/Class;
#12 = Fieldref #57.#58 // java/lang/System.out:Ljava/io/PrintStream;
#13 = Methodref #59.#60 // java/io/PrintStream.println:(D)V