动态编译和静态编译是两种不同的编译方式。
静态编译是指在程序运行之前,将源代码编译成目标代码,并生成可执行文件。这个过程中需要考虑到程序的输入和输出,所以编译器在编译时需要进行类型检查和语法检查等工作,以保证编译出来的代码的正确性和安全性。编译出的目标代码是与特定操作系统和硬件平台相关的,因此不能跨平台执行。
动态编译是指在程序运行的过程中,将源代码或者中间代码通过解释或者编译的方式转化成机器码或者字节码并执行。这个过程中不需要将代码编译成可执行文件,大部分源码甚至不需要完全翻译成目标代码,因此解释器和运行时环境占用的内存较小。由于动态编译器可以在运行时进行调整代码翻译方式和程序行为,因此代码的执行效率可以更高,且具有更好的灵活性。
在 Java 中,Java 源代码首先会被编译成 Java 字节码,然后在 JVM 中通过 Java 解释器执行,也可以通过 JIT 编译器将 Java 字节码转换为本地机器代码,提高程序的执行速度。这种方式既有和静态编译相似之处,也有一定的动态编译特征。
总之,静态编译是在程序运行前,将源代码或中间代码编译为目标代码的过程,程序运行时不需要再次编译。而动态编译是在程序运行时,将源代码或中间代码翻译成机器码或者字节码并执行的过程,具有更高的执行效率和更好的灵活性。
动态编译和静态编译都有各自的优缺点,适用于不同的场景。
动态编译的优点:
动态编译的缺点:
静态编译的优点:
静态编译的缺点:
综上所述,动态编译和静态编译各有其优缺点,开发者应针对不同场景选择最适合的编译方式。例如,Java 采用了 JIT 编译方式,在实际应用中取得了很好的效果,但在某些特殊场景下可以选择采用 AOT 编译加速启动和提高性能。
AOT(Ahead-of-Time)和JIT(Just-in-Time)都是编译器技术,用于将高级语言代码转换为机器可执行的代码。它们在编译和执行代码的时候有着不同的方式和优缺点。
AOT是一种静态编译技术,即在代码运行前进行编译。 AOT编译器将高级语言代码转换为本地机器码,这种代码可以直接在目标机器上运行。AOT编译器执行的代码比JIT编译器执行的代码更快,因为它们不需要在运行时进行编译。 AOT编译器通常用于嵌入式系统和移动设备,因为这些设备的资源有限,需要更高效的代码执行。
JIT是一种动态编译技术,即在程序运行时进行编译。 JIT编译器将高级语言代码转换为本地机器码,然后将其存储在缓存中,以便在需要时直接执行。JIT编译器的优点是可以根据程序的运行情况进行优化,例如通过动态内联函数和优化循环等。 这意味着JIT编译器可以生成更高效的代码,但是它需要在程序运行时进行编译,这会导致一些性能损失。
总体而言,AOT编译器更适用于需要快速启动和高性能的应用程序,而JIT编译器更适用于需要更高级别的优化和动态编译的应用程序。
在 Java 中,JVM 可以理解的代码就叫做字节码(即扩展名为 .class 的文件),它不面向任何特定的处理器,只面向虚拟机。Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以, Java 程序运行时相对来说还是高效的(不过,和 C++,Rust,Go 等语言还是有一定差距的),而且,由于字节码并不针对一种特定的机器,因此,Java 程序无须重新编译便可在多种不同操作系统的计算机上运行。
我们需要格外注意的是 .class->机器码 这一步。在这一步 JVM 类加载器首先加载字节码文件,然后通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被调用的(也就是所谓的热点代码),所以后面引进了 JIT(just-in-time compilation) (Java即时编译器),而 JIT 属于运行时编译。当 JIT 编译器完成第一次编译后,其会将字节码对应的机器码保存下来,下次可以直接使用。而我们知道,机器码的运行效率肯定是高于 Java 解释器的。这也解释了我们为什么经常会说 Java 是编译与解释共存的语言 。
HotSpot 采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系统资源的只有那一小部分的代码(热点代码),而这也就是JIT所需要编译的部分。JVM 会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。JDK 9 引入了一种新的编译模式 AOT(Ahead of Time Compilation),它是直接将字节码编译成机器码,这样就避免了 JIT 预热等各方面的开销。JDK 支持分层编译和 AOT 协作使用。
AOT 可以提前编译节省启动时间,那为什么不全部使用这种编译方式呢?
AOT 编译的优点包括启动速度快、代码性能稳定等,但也有一些缺点:
AOT 编译器需要额外的时间和资源来进行编译。这意味着代码更新和发布需要额外的时间。
AOT 编译器可能会增加二进制文件的大小,从而占用更多的存储空间。
AOT 编译器无法动态地优化代码。如果程序需要处理大量的变量和函数,那么使用 AOT 编译可能会导致代码冗长和缓慢。
AOT 编译器可能会导致操作系统的故障或崩溃,具体原因是由于编译时出现的错误或编译器本身的问题。
因此,选择 AOT 编译还是 JIT 编译取决于应用程序的需求。对于一些需要较高性能的、小型应用程序,AOT 编译可能是比较好的选择。但对于大型应用程序,由于其代码量相对较大,更倾向于 JIT 编译,因为它能够优化动态性和可调性。
我们可以将高级编程语言按照程序的执行方式分为两种:
编译型 : 编译型语言会通过编译器将源代码一次性翻译成可被该平台执行的机器码。一般情况下,编译语言的执行速度比较快,开发效率比较低。常见的编译性语言有 C、C++、Go、Rust 等等。
解释型 : 解释型语言会通过解释器一句一句的将代码解释(interpret)为机器代码后再执行。解释型语言开发效率比较快,执行速度比较慢。常见的解释性语言有 Python、JavaScript、PHP 等等。
Java 语言既具有编译型语言的特征,也具有解释型语言的特征。因为 Java 程序要经过先编译,后解释两个步骤,由 Java 编写的程序需要先经过编译步骤,生成字节码(.class 文件),这种字节码必须由 Java 解释器来解释执行。而且在运行过程中,将字节码解释成机器码的同时也会结合JIT编译器进行优化。
Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,Java程序无须重新编译便可在多种不同的计算机上运行。