③JVM 类文件结构
③JVM 类文件结构
学习核心
- 类文件结构(了解)
学习资料
核心概念
无关性的基础:虚拟机+字节码
平台无关性:Java从诞生之初就有一个口号“一次编写,到处运行”,也就是说你编写的Java程序不用考虑是要运行到什么类型的操作系统上,都是可以运行的
语言无关性:Java虚拟机(JVM)只能执行Java代码编写的程序码?不是的,Java虚拟机只关心字节码文件,程序的运行是虚拟机解释执行字节码文件来完成的。至于字节码文件怎么生成的,虚拟机就不做限制了。目前能够生成字节码文件的语言有(Java,Jruby,Groovy,Kotlin)等。
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 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 // 访问标志:是否为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
图形化工具直观地查看字节码内容:jclasslib
可以下载UI工具查看,也可借助idea引入插件