即时编译JIT

即时编译(Just-In-Time Compilation,简称JIT)是一种编译技术,它在代码即将首次执行时进行编译,因此得名“即时编译”。JIT是动态编译的一种特例。随着时间的发展,JIT的概念已经扩展,现在常被用来泛指动态编译;然而,狭义的JIT编译与更广泛的动态编译之间仍存在区别。动态编译(Dynamic Compilation)指的是在程序运行时进行编译,而静态编译(Static Compilation,也称事前编译,Ahead-Of-Time Compilation,简称AOT)则是在程序运行前完成编译。自适应动态编译(Adaptive Dynamic Compilation)也是一种动态编译技术,它先让程序以某种方式运行,收集信息后再进行编译,从而实现更高层次的优化。

概述

在某些商业虚拟机(如HotSpot)中,Java程序最初是通过解释器(Interpreter)解释执行的。当虚拟机发现某个方法或代码块被频繁调用时,这些代码就会被标记为“热点代码”。为了提高热点代码的执行效率,虚拟机会在运行时将这些代码编译成针对本地平台优化的机器码,这一过程由即时编译器(Just-In-Time Compiler,简称JIT编译器)完成。

即时编译器并不是Java虚拟机的必需组件。Java虚拟机规范并没有规定虚拟机内必须包含即时编译器,也没有对其实现方式提出具体要求。然而,即时编译器的性能和代码优化能力是衡量一个商业虚拟机优劣的重要指标之一,也是虚拟机技术实力的核心体现。除非特别说明,本文提到的编译器和即时编译器均指HotSpot虚拟机中的即时编译器,而虚拟机也特指HotSpot虚拟机。

解释编译并存架构

尽管并非所有Java虚拟机都采用解释器与编译器并存的架构,但许多主流的商业虚拟机(如HotSpot)都同时包含解释器和编译器。解释器和编译器各具优势:

  • 解释器:在程序需要迅速启动和执行时,解释器可以立即开始工作,避免编译所需的时间。
  • 编译器:随着程序运行时间的增长,编译器逐渐发挥作用,将越来越多的代码编译成本地代码,从而获得更高的执行效率。

在内存资源有限的环境中(如嵌入式系统),可以使用解释器执行以节省内存;在其他情况下,可以利用编译执行来提升性能。此外,如果编译后的代码遇到罕见问题,系统还可以通过回退到解释执行来解决问题。

编译的时间与空间开销

时间开销

  • 解释执行:输入的代码 → 解释器解释执行 → 执行结果
  • JIT编译再执行:输入的代码 → 编译器编译 → 编译后的代码 → 执行 → 执行结果

JIT编译后的代码执行速度通常比解释执行快,但这并不意味着编译过程本身比解释快。对于只执行一次的代码,解释执行通常更快,因为编译过程会带来额外的开销。只有对频繁执行的代码,JIT编译才能带来正向收益。

空间开销

编译后的代码通常比字节码大得多,膨胀比达到10倍是常见现象。因此,只有对频繁执行的代码进行编译才有意义,否则会显著增加代码占用的空间。

不同的编译器

HotSpot虚拟机内置了两个即时编译器:

  • **Client Compiler (C1)**:适用于客户端应用,注重编译速度。
  • **Server Compiler (C2)**:适用于服务端应用,注重编译质量。

虚拟机会根据自身版本和宿主机器的硬件性能自动选择运行模式,用户也可以通过“-client”或“-server”参数强制指定虚拟机运行在客户端模式或服务端模式。提供多个即时编译器的原因与提供多个垃圾收集器类似,都是为了适应不同的应用场景。

热点代码

定义

热点代码是指在程序运行过程中被频繁调用的代码段,包括方法和循环体。这些代码的执行频率高,对程序的整体性能影响较大。

识别热点代码

JIT编译器通过以下方式识别热点代码:

  • 基于计数器的热点探测:为每个方法和循环体设置计数器,统计其执行次数。当计数器超过一定阈值时,该代码段被标记为热点代码。
  • 基于采样的热点探测:周期性地检查各个线程的栈顶,如果发现某些方法经常出现在栈顶,则认为这些方法是热点方法。

在HotSpot虚拟机中,主要采用基于计数器的热点探测方法。每个方法有两个计数器:方法调用计数器和回边计数器。当计数器超过阈值时,会触发JIT编译。

方法调用计数器

方法调用计数器用于统计方法被调用的次数。当一个方法被调用时,会先检查该方法是否有已编译的版本,如果有则优先使用编译后的版本。如果没有,则将调用计数器加1,并判断是否超过阈值。如果超过阈值,将向即时编译器提交编译请求。编译完成后,该方法的调用入口地址会被自动更新为新的版本。

回边计数器

回边计数器用于统计方法中循环体代码的执行次数。在字节码中,遇到控制流向后跳转的指令称为“回边”。回边计数器的值与方法调用计数器的值相加,当总和超过阈值时,也会触发JIT编译。

总结

即时编译技术通过动态编译热点代码,显著提升了程序的执行效率。HotSpot虚拟机中的即时编译器通过解释器与编译器并存的架构,结合基于计数器的热点探测方法,有效地识别和优化热点代码,从而在不同的应用场景中提供高性能的解决方案。