Java代码为何在虚拟机中运行?

因为提供了可移植性,一旦Java代码被编译为Java字节码就可以在不同平台上的Java虚拟机中运行。同时虚拟机还提供了一个代码托管的环境,代替程序员来处理一些冗长且容易出错的事务,如内存管理。
(Write Once,Run Anywhere.)

Java代码在虚拟机中如何运行?

JVM 将运行时内存区域划分为 方法区、堆 、PC寄存器、Java方法栈、本地方法栈(后三个为线程私有)。Java程序编程成class文件后,需先加载到方法区中,然后才能在 JVM 中运行。

Java 虚拟机的运行效率是怎样的?

HotSpot 采用了多种技术来提升启动性能以及峰值性能,即时编译便是其中最重要的技术之一

在标准JDK中的 HotSpot 虚拟机中,采用的是混合执行策略以提高执行效率:

  • 第一种是解释执行,即逐条翻译字节码为机器码来执行;第二种是即时编译(Just-In-Time compilation ,JIT),即将一个方法中包含的所有字节码编译成机器码后再执行。
  • 解释执行的优势在于无需等待编译,JIT的优势在于实际运行速度更快。
  • HotSpot默认采用混合模式,综合了两者的优势;其会先解释执行字节码,然后将其中反复执行的热点代码以方法为单位来进行即时编译。

即时编译建立在程序符合二八定律的假设上,即百分之二十的代码占用了百分之八十的计算资源。对于大部分的不常用代码,无需耗费时间将其编译成机器码,采用解释执行的方式运行即可;对于仅占据小部分的热点代码,将其编译成机器码运行则能达到理想的运行速度。

理论上讲,即时编译后的 Java 程序的执行效率,是可能超过 C++ 程序的。这是因为与静态编译相比,即时编译拥有程序的运行时信息,并且能够根据这个信息做出相应的优化。
举个例子,我们知道虚方法是用来实现面向对象语言多态性的。对于一个虚方法调用,尽管它有很多个目标方法,但在实际运行过程中它可能只调用其中的一个。
这个信息便可以被即时编译器所利用,来规避虚方法调用的开销,从而达到比静态编译的 C++ 程序更高的性能。

Java虚拟机内置的编译器?

为了满足不同用户场景的需要,HotSpot 内置了多个即时编译器:C1、C2 和 Graal。
之所以引入多个即时编译器,是为了在编译时间和生成代码的执行效率之间进行取舍。

  • C1:又叫 Client 编译器,面向的是对启动性能有要求的客户端GUI程序,采用的优化手段相对简单,所以编译时间较短。
  • C2:又叫Server编译器,面向的是对峰值性能有要求的服务器端程序,采用的优化手段相对复杂,因此编译时间较长,但同时生成代码的执行效率较高。

从 Java 7 开始,HotSpot 默认采用分层编译的方式:热点方法首先会被 C1 编译,而后热点方法中的热点会进一步被 C2 编译。
为了不干扰应用的正常运行,HotSpot 的即时编译是放在额外的编译线程中进行的。HotSpot 会根据 CPU 的数量设置编译线程的数目,并且按 1:2 的比例配置给 C1 及 C2 编译器。

在计算资源充足的情况下,字节码的解释执行和即时编译可同时进行。编译完成后的机器码会在下次调用该方法时启用,以替换原本的解释执行。