Java代码是怎么运行的?
运行流程
将Java程序转换成Java虚拟机所能识别的指令序列,也称Java字节码。之所以这么取名,是因为Java字节码指令的操作码(opcode)被固定为一个字节。
Java虚拟机可以由硬件实现,但更为常见的是在各个现有平台(如Windows_x64、Linux_aarch64)上提供软件实现。
Java虚拟机将运行时内存区域划分为五个部分,分别为方法区、堆、PC寄存器、Java方法栈和本地方法栈。
执行Java代码首先需要将它编译而成的class文件加载到Java虚拟机中。加载后的Java类会被存放于方法区(Method Area)中。实际运行时,虚拟机会执行方法区内的代码。
Java虚拟机将栈分为面向Java方法的Java方法栈,面向本地方法(用C++写的native方法)的本地方法栈,以及存放各个线程执行位置的PC寄存器。
注:PC寄存器并不是物理寄存器,本质是内存空间,只是对物理PC寄存器的一种抽象模拟。
在JVM规范中,每个线程都有它自己的程序计数器,是线程私有的,生命周期与线程的生命周期保持一致。
在运行过程中,每当调用进入一个Java方法,Java虚拟机会在当前线程的Java方法栈中生成一个栈帧,用以存放局部变量以及字节码的操作数。这个栈帧的大小是提前计算好的,而且Java虚拟机不要求栈帧在内存空间里连续分布。
当退出当前执行的方法时,不管是正常返回还是异常返回,Java虚拟机均会弹出当前线程的当前栈帧,并将之舍弃。
字节码->机器码
在HotSpot里面,Java虚拟机需要将字节码翻译成机器码,有两种形式:第一种是解释执行,逐条将字节码翻译成机器码并执行;第二种是即时编译(Just-In-Time compilation,JIT),将一个方法中包含的所有字节码编译成机器码后再执行。
HotSpot默认采用混合模式,它会先解释执行字节码,而后将其中反复执行的热点代码,以方法为单位进行即时编译,翻译成机器码后直接运行在底层硬件之上。
即时编译建立在程序符合二八定律的假设上,也就是百分之二十的代码占据了百分之八十的计算资源。
对于占据大部分的不常用的代码,我们无需耗费时间将其编译成机器码,而是采取解释执行的方式运行;另一方面,对于仅占据小部分的热点代码,我们则可以将其编译成机器码,以达到理想的运行速度。
HotSpot内置了多个即时编译器:C1、C2和Graal。
之所以引入多个即时编译器,是为了在编译时间和生成代码的执行效率之间进行取舍。C1又叫做Client编译器,面向的是对启动性能有要求的客户端GUI程序,采用的优化手段相对简单,因此编译时间较短。
C2又叫做Server编译器,面向的是对峰值性能有要求的服务器端程序,采用的优化手段相对复杂,因此编译时间较长,但同时生成代码的执行效率较高。
从Java 7开始,HotSpot默认采用分层编译的方式:热点方法首先会被C1编译,而后热点方法中的热点会进一步被C2编译。
为了不干扰应用的正常运行,HotSpot的即时编译是放在额外的编译线程中进行的。HotSpot会根据CPU的数量设置编译线程的数目,并且按1:2的比例配置给C1及C2编译器。
在计算资源充足的情况下,字节码的解释执行和即时编译可同时进行。编译完成后的机器码会在下次调用该方法时启用,以替换原本的解释执行。
小实验
通过观察两个条件判断语句的运行结果,来思考Java语言和Java虚拟机看待boolean类型的方式是否不同。
public class Foo {
public static void main(String[] args) {
boolean flag = true;
if (flag)
System.out.println("Hello, Java!");
if (flag == true)
System.out.println("Hello, JVM!");
}
}
E:\Test\asm>javac Foo.java
E:\Test\asm>java Foo
Hello, Java!
Hello, JVM!
E:\Test\asm>java -cp E://Test/asm/asmtools.jar org.openjdk.asmtools.jdis.Main Foo.class > Foo.jasm
E:\Test\asm>java -cp E://Test/asm/asmtools.jar org.openjdk.asmtools.jasm.Main Foo.jasm > Foo.class
E:\Test\asm>java Foo
Hello, Java!
E:\Test\asm>
注1:java -cp 命令的主要用途是加载类和类库。
java -cp 命令的基本语法是:java -cp [options]。其中,参数是类和类库的路径,参数是Java应用程序的启动类,[options]参数是可选的,用于指定虚拟机的额外参数。
注2:asmtools的下载地址:https://wiki.openjdk.org/display/CodeTools/asmtools