在部分的商用虚拟机中,Java 程序最初是通过解释器(Interpreter )进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁的时候,就会把这些代码认定为“热点代码”。为了提高热点代码的执行效率,在运行时,即时编译器(Just In Time Compiler )会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化。
一.HotSpot 内的即时编译器
1.解释器和编译器各有各的优点:
解释器优点:当程序需要迅速启动的时候,解释器可以首先发挥作用,省去了编译的时间,立即执行。解释执行占用更小的内存空间。同时,当编译器进行的激进优化失败的时候,还可以进行逆优化来恢复到解释执行的状态。
编译器优点:在程序运行时,随着时间的推移,编译器逐渐发挥作用根据热点探测功能,,将有价值的字节码编译为本地机器指令,以换取更高的程序执行效率。
因此,整个虚拟机执行架构中,解释器与编译器经常配合工作,如下图所示。
HotSpot中内置了两个即时编译器,分别称为 Client Compiler和 Server Compiler ,或者简称为 C1编译器和 C2编译器。目前的 HotSpot编译器默认的是解释器和其中一个即时编译器配合的方式工作,具体是哪一个编译器,取决于虚拟机运行的模式,HotSpot虚拟机会根据自身版本与计算机的硬件性能自动选择运行模式,用户也可以使用 -client和 -server参数强制指定虚拟机运行在 Client模式或者 Server模式。这种配合使用的方式称为“混合模式”(Mixed Mode),用户可以使用参数 -Xint 强制虚拟机运行于 “解释模式”(Interpreted Mode),这时候编译器完全不介入工作。另外,使用 -Xcomp 强制虚拟机运行于 “编译模式”(Compiled Mode),这时候将优先采用编译方式执行,但是解释器仍然要在编译无法进行的情况下接入执行过程。通过虚拟机 -version 命令可以查看当前默认的运行模式。
二.被编译对象和触发条件
1.在运行过程中会被即时编译的“热点代码”有两类,即:
- 被多次调用的方法
- 被多次执行的循环体
对于第一种,编译器会将整个方法作为编译对象,这也是标准的JIT 编译方式。对于第二种是由循环体出发的,但是编译器依然会以整个方法作为编译对象,因为发生在方法执行过程中,称为栈上替换。
判断一段代码是否是热点代码,是不是需要出发即时编译,这样的行为称为热点探测(Hot Spot Detection),探测算法有两种,分别为。
基于采样的热点探测(Sample Based Hot Spot Detection):虚拟机会周期的对各个线程栈顶进行检查,如果某些方法经常出现在栈顶,这个方法就是“热点方法”。好处是实现简单、高效,很容易获取方法调用关系。缺点