Java工程师知识树

Java基础-执行引擎-即时编译器

2021-03-08  本文已影响0人  HughJin

Java工程师知识树 / Java基础


即时编译器

在部分的商用虚拟机中,比如HotSpot中,Java程序先通过解释器(Interceptor)进行解释执行。这也是为什么称Java是基于解释执行的原因。但当虚拟机发现某块代码或方法运行的特别频繁,便会将其标记为“热点代码”(Hot Spot Code)。

针对热点代码,虚拟机会采用各种措施来提升其执行效率,因为执行比较频繁,如果能够提升其执行效率,性价比还是比较高的。为此,在运行时,虚拟机会把这些代码编译成与本地平台相关的机器码,并进行各层次的深度优化。而这些优化操作便是通过编译器来完成的,也称作即使编译器(Just In Time Compiler,简称 JIT 编译器)。

即时编译器并不是虚拟机必须的部分,Java虚拟机规范并没有规定Java虚拟机内必须要有即时编译器存在,更没有限定或指导即时编译器应该如何去实现。但是,即时编译器编译性能的好坏、代码优化程度的高低却是衡量一款商用虚拟机优秀与否的最关键的指标之一,它也是虚拟机中最核心且最能体现虚拟机技术水平的部分。

因此,准确的来说,像HotSpot等虚拟机,Java是基于解释执行和编译执行的。下面用一张图来解释该过程:

既然即时编译器进行了各层次的优化,那么为什么Java还使用解释器来“拖累”程序的性能呢?

这是因为,解释器与编译器两者各有优势:当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即执行。当程序运行环境中内存资源限制较大(如部分嵌入式系统中),可以使用解释器执行节约内存,反之可以使用编译执行来提升效率。此外,如果编译后出现“罕见陷阱”,可以通过逆优化退回到解释执行。

Java虚拟机运行时,解释器和即时编译器能够相互协作,取长补短。无论采用解释器进行解释执行,还是采用即使编译器进行编译执行,最终字节码都需要被转换为对应平台的本地机器码指令。某些服务并不看重启动时间,而某些服务却非常看重,这就需要采用解释器与即时编译器并存来换取一个平衡点。

我们可以从解释器和编译器的编译时间开销和编译空间开销两方面进行对比。首先,看编译的时间开销。

我们所说的JIT比解释器快,仅限于对“热点代码”编译之后的代码执行起来要比解释器解释执行的快。通过上图可以看出,如果是只是单次执行的代码,JIT编译比解释器要多出一步“执行编译”,因此,只执行一次时,JIT是要比解释器慢的。只执行一次的代码通常包括只被调用一次的代码(比如构造器)、没有循环的代码等,此时使用JIT显然得不偿失。

其次,再来看看编译空间方面的开销。对一般的Java方法而言,编译后代码的大小相对于字节码,膨胀比达到10倍是很正常的。只有对执行频繁的代码才值得编译,如果把所有代码都编译则会显著增加代码所占空间,导致“代码爆炸”。这就是为什么有些JVM不会单一使用JIT编译,而是选择用解释器+JIT编译器的混合执行引擎。

HotSpot的两种即时编译器

HotSpot虚拟机为了使用不同的应用场景,内置了两个即时编译器:Client Complier和Server Complier,简称为C1、C2编译器,分别用在客户端和服务端。

Client Complier可获取更高的编译速度,Server Complier可获取更好的编译质量。

JVM Server模式与client模式最主要的差别在于:-server模式启动时,速度较慢,但是一旦运行起来后,性能将会有很大的提升。原因是:当虚拟机运行在-client模式时,使用的是一个代号为C1的轻量级编译器,而-server模式启动的虚拟机采用相对重量级代号为C2的编译器。C2比C1编译器编译的相对彻底,服务起来之后,性能更高。

默认情况下,使用C1还是C2编译器,要取决于虚拟机运行的模式。HotSpot虚拟机会根据自身版本与宿主机器的硬件性能自动选择运行模式,用户也可以使用“-client”或“-server”参数去强制指定虚拟机运行在Client模式或Server模式。

目前主流的HotSpot虚拟机中默认是采用解释器与其中一个编译器配合的方式工作,这种配合称作混合模式(Mixed Mode)。用户可以使用参数-Xint强制虚拟机运行于 “解释模式”(Interpreted Mode),这时候编译器完全不介入工作。使用-Xcomp强制虚拟机运行于 “编译模式”(Compiled Mode),这时将优先采用编译方式执行,但是解释器仍然要在编译无法进行的情况下接入执行过程。通过虚拟机java -version命令可以查看当前默认的运行模式。

在上述示例中我们不仅能够看到采用的模式为mixed mode,还能看到出采用的是Server模式。

什么是热点代码?

热点探测

上面解释了JIT编译器的基本功能,那么它是如何判断热点代码的呢?判断一段代码是不是热点代码的行为,也叫热点探测(Hot Spot Detection),通常有两种方法:基于采样的热点探测基于计数器的热点探测(HotSpot使用此方式)

HotSpot虚拟机默认采用基于计数器的热点探测,有两种计数器:方法调用计数器和回边计数器。当计数器数值大于默认阈值或指定阈值时,方法或代码块会被编译成本地代码。

不同模式的性能对比

了解JVM的不同编译模式,不同编译器的性能。

通过添加虚拟机参数“-XX:+PrintCompilation”来打印编译信息。
纯解释执行模式,JVM参数添加“-Xint -XX:+PrintCompilation”
采用编译器模式执行,修改虚拟机参数:“-Xcomp -XX:+PrintCompilation”
采用混合模式执行,修改虚拟机参数:“-XX:+PrintCompilation”

各种模式的耗时由小到大顺序为:混合模式<纯编译模式<纯解释模式。

编译优化技术

即时编译器之所以快,在编译本地代码时,使用了优化措施,产生的本地代码会比 javac 产生的字节码更优秀。

下面看一下即时编译器在生产本地代码时都采用了哪些优化技术。

小结

通过运作原理、使用场景、使用流程、判断代码、优化技术等方面进行阐释即时编译器.主要内容是即时编译器的运作原理与优化技术.

一句话概述:
通过JIT编译器在运行时把Class文件字节码编译成本地机器码。使用基于计数器的热点探测方法探测热点代码。用解释器+JIT编译器的混合执行引擎的原因是可以提高执行效率。JIT编译器在编译本地代码时使用了公共子表达式消除/数组范围检查消除/方法内联/逃逸分析等优化措施。

上一篇 下一篇

猜你喜欢

热点阅读