即时编译是一种提升解释型程序性能的方法。在执行的时候,可以将程序编译为本机代码以提升其性能。即时编译也被称为动态编译。

动态编译比静态编译具有一些优势。在运行 Java 或 C#应用程序时,运行时环境可以对其进行概要分析,以生成更多优化的代码。如果应用程序在运行时行为发生了变化,则运行时环境可以重新编译代码。

即时编译的缺点包括启动延迟和运行时编译的资源。为了降低使用资源,许多即时编译器仅编译经常使用的代码路径。

概述

传统上,有两种方法可以将源代码转换为可以在平台上运行的形式。静态编译将代码转换为特定平台的语言。解释器直接执行源代码。

即时编译尝试利用两者的优势。在运行解释型程序时,即时编译器确定最常用的代码并将其编译为机器代码,可以通过某种方法或较小的代码段完成。

动态编译最初是由 J. McCarthy 在 1960 年的一篇关于 LISP 的论文中提到的。

即时编译,即时或动态翻译是在程序执行期间进行的编译,意思是在运行时,而不是在执行之前,将程序翻译成机器代码。即时的优点在于,由于编译是在运行时进行的,因此即时编译器可以访问动态运行时信息,从而可以进行更好的优化(例如内联函数)。

关于即时编译,有一个重要之处是它将字节码编译为正在运行的机器的机器代码指令。意思是,生成的机器代码针对正在运行的机器的 CPU 架构进行了优化。

即时编译器的一些示例是 Java 中的 JVM(Java 虚拟机)和 C#中的 CLR(公共语言运行库)。

历史

最初,编译器负责将高级语言(比汇编程序高的语言)转换为目标代码(机器指令),然后将其(通过链接器)链接为可执行文件。

在语言发展的某一时期,编译器将高级语言编译为伪代码,然后将其解释(由解释器)以运行你的程序。这消除了目标代码和可执行文件,并允许这些语言可移植到多个操作系统和硬件平台。

Pascal(编译为 P-Code)是最早的一个。Java 和 C# 是最近的示例。最终,术语 P-Code 被替换为字节码,因为大多数伪操作都是一个字节长。

即时(即时)编译器是运行时解释器的功能,它不会在每次调用方法时都解释字节码,而是将字节码编译为正在运行的计算机的机器代码指令,然后调用此目标代码。理想情况下,运行目标代码的效率将克服每次运行时重新编译程序的效率低下的问题。

典型场景

源代码已完全转换为机器代码。

即时场景

源代码将被转换为汇编语言,例如结构 [对于 C#来说,是用于 ex IL(中间语言),对于 Java,是 ByteCode)。

仅当应用程序的需求(即需要的代码)被转换为机器代码时,中间代码才被转换为机器语言。

比较即时和非即时

在即时编译中,并非所有代码都首先转换为机器代码。一部分必需的代码将被转换为机器代码,然后,如果所调用的方法或功能不在机器中,则将其转换为机器代码,从而减轻 CPU 的负担。由于机器代码将在运行时生成,因此即时编译器将生成针对机器的 CPU 体系结构进行了优化的机器代码。

即时编译的一些示例:

  • Java:JVM(Java 虚拟机)
  • C#:CLR(公共语言运行时)
  • Android:较新版本的 DVM(Dalvik 虚拟机)或 ART(Android 运行时)

Java 虚拟机(JVM)执行字节码并维护一个函数执行多少次的计数。如果此计数超过预定义的限制,则即时编译将代码编译为机器语言,该语言可以直接由处理器执行(不同于正常情况下, javac 将该代码编译为字节码,然后再编译为 Java,解释器会逐行解释此字节码,然后将其转换为机器代码并执行)。

同样,下次计算此函数时,将再次执行相同的编译代码。这与正常解释不同,在正常解释中,代码逐行再次解释。前者使执行速度更快。

原文:Just in Time Compilation Explained