Java虚拟机的运行效率
HotSpot采用了多种技术来提升启动性能以及峰值性能,即时编译便是其中最重要的技术之一。
即时编译建立在程序符合二八定律,百分之二十的代码占据了百分之八十的计算资源。
- 对占据大部分的不常用的代码,无需耗费时间将其编译成机器码,而是采取解释执行的方式运行
- 对于仅占据小部分的热点代码,我们则可以将其编译成机器码,以达到理想运行速度。
理论即时编译后的Java程序的执行效率,是可能超过C++。因为与静态编译相比,即时编译拥有程序的运行时信息,并且能够根据这个信息做出相应的优化。
虚方法是用来实现多态性。对一个虚方法调用,尽管有很多目标方法,但实际运行过程中,可能只调用其中一个。
这信息可被即时编译器所利用,规避虚方法调用的开销,达到比静态编译的C++程序更高的性能。
为满足不同用户场景的需要,HotSpot内置了多个即时编译器:C1、C2和Graal。
Graal是Java 10正式引入的实验性即时编译器。
之所以引入多个即时编译器,为在编译时间和生成代码的执行效率之间进行取舍。C1又叫做Client编译器,面向对启动性能有要求的客户端GUI程序,采用的优化手段相对简单,因此编译时间较短。
C2又叫做Server编译器,面向对峰值性能有要求的服务器端程序,采用的优化手段相对复杂,因此编译时间较长,但同时生成代码的执行效率较高。
从Java 7开始,HotSpot默认采用分层编译的方式:热点方法首先会被C1编译,而后热点方法中的热点会进一步被C2编译。
为了不干扰应用的正常运行,HotSpot的即时编译是放在额外的编译线程中进行的。HotSpot会根据CPU的数量设置编译线程的数目,并且按1:2的比例配置给C1及C2编译器。
在计算资源充足的情况下,字节码的解释执行和即时编译可同时进行。编译完成后的机器码会在下次调用该方法时启用,以替换原本的解释执行。
总结
在虚拟机中运行,是因为它提供了可移植性。一旦Java代码被编译为Java字节码,便可以在不同平台上的Java虚拟机实现上运行。此外,虚拟机还提供了一个代码托管的环境,代替我们处理部分冗长而且容易出错的事务,例如内存管理。
Java虚拟机将运行时内存区域划分为五个部分,分别为方法区、堆、PC寄存器、Java方法栈和本地方法栈。Java程序编译而成的class文件,需要先加载至方法区中,方能在Java虚拟机中运行。
为了提高运行效率,标准JDK中的HotSpot虚拟机采用的是一种混合执行的策略。
它会解释执行Java字节码,然后会将其中反复执行的热点代码,以方法为单位进行即时编译,翻译成机器码后直接运行在底层硬件之上。
HotSpot装载了多个不同的即时编译器,以便在编译时间和生成代码的执行效率之间做取舍。
参考
- [1]https://en.wikipedia.org/wiki/Java_processor
- [2]https://wiki.openjdk.java.net/display/CodeTools/asmtools