一、前言
刚开始学习Java的时候老师告诉我们Java是跨平台语言,一次编译到处运行,那么在Java编译过程中做了什么事情,而虚拟机又是怎么初始化和创建对象的,这里对我知道的做一个记录和梳理,如有理解错误,欢迎评论指出!
要将编写的Java源码要运行起来,首先要经过Java编译器编译。将原来的.java文件经过编译器编译转换成.class字节码文件。编译器的存在主要就是编译不同的源文件,将其转换成能在Java虚拟机上运行的.class文件,不同的语言有不同的编译器,同是转换成能在虚拟机上运行的.class文件。
生成的.class文件为后面的虚拟机加载提供很多前期基础,首先知道虚拟机的加载类过程是动态加载的,也就是在使用到当前类或接口的时候再进行加载。加载的时候需要获取到当前类的符号引用,根据符号引用在创建时或运行时解析、翻译到具体的内存地址中去,也就是为对象分配具体内存地址。
符号引用来源于常量池,这里的常量池可以理解为Class文件中的资源仓库。常量池中主要存放两大类常量:字面量和符号引用,字面量比较接近Java语言层面的常量概念,而符号引用接近于Java语言层面的常量概念,主要包含3类常量
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
下面的 #1 = Class #2 // com/app/MainAction 就是该类的全限定名,在对象创建的时候比如遇到New关键字,JVM首先对符号引用进行解析,这里就会用到全限定名。如果找不到对应的符号引用,那么这个类就还没有加载,因此jvm就会进行加载过程。当在运行时解析完符号引用后,JVM就会在堆中为对象分配内存。
对于编译器编译后的.class文件使用javap 工具能看到相应的反汇编结果
但在介绍反汇编结果前首先说一下Class的整个文件结构,在Class文件结构中,总共有7类数据,分别代表着类的不同信息
- 魔数与Class文件的版本:用来确定这个文件是否为一个能被虚拟机接受的Class文件
- 常量池:它是Class文件结构中与其他项目关联最对的数据类型,主要包含的就是字面量和符号引用
- 访问标志:指明当前类是接口还是类,是否定义为public类型,是或定义为abstract类型,如果是类是否定义为final
- 类索引、父类索引与接口索引集合:类索引指明当前类的全限定名,父类索引指当前类继承的父类全限定名,接口索引集合表明当前类所实现的接口集合
- 字段表集合:用于描述接口和类中声明的变量,字段包括类级变量以及实例变量,但不包括在方法里面声明的局部变量,字段表和方法表采用同样的数据结构,依次包括访问标志,名称索引,描述符索引,属性表集合。
- 方法表集合:结构方式与字段表一样,方法中的java代码在编译成字节码后,存放在方法属性表集合中名为“Code”的属性里。
- 属性表集合 : 1、Code 属性 2、Exceptions属性 3、LineNumberTable属性 4、LocalVariableTable属性 5 SourceFile属性 6 ConstantValue 属性7InnerClasses属性 8、DepreCated以及Synthetic属性 9 StackMapTable属性 10 Signature属性 11BootstrapMethods属性
其中Code:属性用于存放Java方法中的字节码,Exceptions属性将类中可能抛出的异常进行列举,对于LocalVariableTable:属性则是栈帧中局部变量表中的变量和java源码中的变量间的关系
SourceFile:用于记录生成这个Class文件的源码名称;ConstantValue属性通知通知虚拟机自动为静态变量赋值,对于static 修饰的变量但不为基础数据类型或者是java.lang.String,会在类构造器方法初始化;而使用了final和static修饰的常量并且是基础数据类型或者是java.lang.String则使用ConstantValue属性初始化,对于非static的变量则在实例构造器中初始化
InnerClasses:用于记录内部和宿主之间的关系,如果定义了内部类就有这个属性
DepreCated以及Synthetic:这两个都是boolean属性,表示有和没有,DepreCated属性用于表示方法和类被弃推荐不再使用
下面是使用javap工具反编译出来的文件,用于分析生成的.Class文件
public class MainAction {
public static void main(String[] args) {
OtherAction othre = new OtherAction();
othre.fun();
}
}
public class com.app.MainAction
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Class #2 // com/app/MainAction
#2 = Utf8 com/app/MainAction
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Methodref #3.#9 // java/lang/Object."<init>":()V
#9 = NameAndType #5:#6 // "<init>":()V
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/app/MainAction;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Class #17 // com/app/OtherAction
#17 = Utf8 com/app/OtherAction
#18 = Methodref #16.#9 // com/app/OtherAction."<init>":()V
#19 = Methodref #16.#20 // com/app/OtherAction.fun:()V
#20 = NameAndType #21:#6 // fun:()V
#21 = Utf8 fun
#22 = Utf8 args
#23 = Utf8 [Ljava/lang/String;
#24 = Utf8 othre
#25 = Utf8 Lcom/app/OtherAction;
#26 = Utf8 SourceFile
#27 = Utf8 MainAction.java
{
public com.app.MainAction();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/app/MainAction;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #16 // class com/app/OtherAction
3: dup
4: invokespecial #18 // Method com/app/OtherAction."<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #19 // Method com/app/OtherAction.fun:()V
12: return
LineNumberTable:
line 6: 0
line 7: 8
line 8: 12
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 args [Ljava/lang/String;
8 5 1 othre Lcom/app/OtherAction;
}
public class OtherAction {
static int age = 1;
int age1 = 2;
static {
System.out.println("这是静态代码块");
}
{
System.out.println("这是普通代码块" + age1);
}
public OtherAction() {
System.out.println("这是构造方法");
}
public static void show() {
System.out.println("这是静态方法");
}
public void fun() {
System.out.println("这是普通方法");
}
}
public class com.app.OtherAction
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Class #2 // com/app/OtherAction
#2 = Utf8 com/app/OtherAction
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Utf8 age
#6 = Utf8 I
#7 = Utf8 age1
#8 = Utf8 <clinit>
#9 = Utf8 ()V
#10 = Utf8 Code
#11 = Fieldref #1.#12 // com/app/OtherAction.age:I
#12 = NameAndType #5:#6 // age:I
#13 = Fieldref #14.#16 // java/lang/System.out:Ljava/io/PrintStream;
#14 = Class #15 // java/lang/System
#15 = Utf8 java/lang/System
#16 = NameAndType #17:#18 // out:Ljava/io/PrintStream;
#17 = Utf8 out
#18 = Utf8 Ljava/io/PrintStream;
#19 = String #20 // 这是静态代码块
#20 = Utf8 这是静态代码块
#21 = Methodref #22.#24 // java/io/PrintStream.println:(Ljava/lang/String;)V
#22 = Class #23 // java/io/PrintStream
#23 = Utf8 java/io/PrintStream
#24 = NameAndType #25:#26 // println:(Ljava/lang/String;)V
#25 = Utf8 println
#26 = Utf8 (Ljava/lang/String;)V
#27 = Utf8 LineNumberTable
#28 = Utf8 LocalVariableTable
#29 = Utf8 <init>
#30 = Methodref #3.#31 // java/lang/Object."<init>":()V
#31 = NameAndType #29:#9 // "<init>":()V
#32 = Fieldref #1.#33 // com/app/OtherAction.age1:I
#33 = NameAndType #7:#6 // age1:I
#34 = Class #35 // java/lang/StringBuilder
#35 = Utf8 java/lang/StringBuilder
#36 = String #37 // 这是普通代码块
#37 = Utf8 这是普通代码块
#38 = Methodref #34.#39 // java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
#39 = NameAndType #29:#26 // "<init>":(Ljava/lang/String;)V
#40 = Methodref #34.#41 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
#41 = NameAndType #42:#43 // append:(I)Ljava/lang/StringBuilder;
#42 = Utf8 append
#43 = Utf8 (I)Ljava/lang/StringBuilder;
#44 = Methodref #34.#45 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#45 = NameAndType #46:#47 // toString:()Ljava/lang/String;
#46 = Utf8 toString
#47 = Utf8 ()Ljava/lang/String;
#48 = String #49 // 这是构造方法
#49 = Utf8 这是构造方法
#50 = Utf8 this
#51 = Utf8 Lcom/app/OtherAction;
#52 = Utf8 show
#53 = String #54 // 这是静态方法
#54 = Utf8 这是静态方法
#55 = Utf8 fun
#56 = String #57 // 这是普通方法
#57 = Utf8 这是普通方法
#58 = Utf8 SourceFile
#59 = Utf8 OtherAction.java
{
static int age;
descriptor: I
flags: ACC_STATIC
int age1;
descriptor: I
flags:
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: iconst_1
1: putstatic #11 // Field age:I
4: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #19 // String 这是静态代码块
9: invokevirtual #21 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: return
LineNumberTable:
line 5: 0
line 9: 4
line 10: 12
LocalVariableTable:
Start Length Slot Name Signature
public com.app.OtherAction();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=4, locals=1, args_size=1
0: aload_0
1: invokespecial #30 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_2
6: putfield #32 // Field age1:I
9: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
12: new #34 // class java/lang/StringBuilder
15: dup
16: ldc #36 // String 这是普通代码块
18: invokespecial #38 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
21: aload_0
22: getfield #32 // Field age1:I
25: invokevirtual #40 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
28: invokevirtual #44 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: invokevirtual #21 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
34: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
37: ldc #48 // String 这是构造方法
39: invokevirtual #21 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
42: return
LineNumberTable:
line 16: 0
line 6: 4
line 13: 9
line 17: 34
line 18: 42
LocalVariableTable:
Start Length Slot Name Signature
0 43 0 this Lcom/app/OtherAction;
public static void show();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #53 // String 这是静态方法
5: invokevirtual #21 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 21: 0
line 22: 8
LocalVariableTable:
Start Length Slot Name Signature
public void fun();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #56 // String 这是普通方法
5: invokevirtual #21 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 25: 0
line 26: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lcom/app/OtherAction;
}