为什么JVM不能预先编译整个程序,而不是逐个编译它?

时间:2011-04-26 18:28:38

标签: java jvm compilation jit bytecode

Herbert Schildt写道:

  

重要的是要理解将整个Java程序一次性编译成可执行代码是不切实际的,因为Java只能在运行时执行各种运行时检查。

他的意思是什么运行时检查?

请解释逐字编译字节码的原因,而不是整个程序。

5 个答案:

答案 0 :(得分:3)

可能有几个理由将它逐个编译(这些是我想到的前两个):

  1. 优化多次使用的代码,并非所有代码都需要重新编译,而只需要重新编译。
  2. 通过网络获取课程 - 有时您希望避免获取所有代码,因为它会带宽。
  3. 我不知道这个网站是否准确,但我从中学到了很多东西:http://www.artima.com/insidejvm/ed2/index.html

答案 1 :(得分:2)

这种推理是不正确的。可以将整个Java程序一次性编译成可执行代码,事实上gcj就是这样。

Java运行时通常不进行前期编译的实际原因:

  • 作为Java“一次编写,随处运行”philosphy的一部分,编译后的代码作为平台无关字节码而不是特定于平台的本机代码进行分发。

  • 启动时将字节码编译为本机代码是次优的,原因有几个。首先,它会产生相当大的启动成本,特别是对于大型程序。此外,现代JIT能够利用运行时分析来产生比使用前端编译器更好的优化。优化通常涉及进行权衡:例如,内联以较大代码为代价消除了函数调用开销(由于缓存性能不佳甚至交换,这可能导致代码速度变慢)。当您在编译时获得运行时分析信息时,您可以更智能地决定哪些权衡有意义。

  • Java API具有依赖于在运行时加载字节码的能力的历史。 (想想任何依赖于Class.forName和/或自定义类加载器的东西,例如,包括applet,servlet容器等。)为了支持这种,需要某种运行时编译器(或解释器)。进行前期编译意味着您要么必须删除对此的支持,要么将其视为一种特殊情况。

答案 2 :(得分:2)

一个可能的原因:Java运行时是非常动态的环境。类一直在加载和卸载。根据当前使用的类,可能或不可能进行某些优化。例如,如果您有接口A和单个实现ImplA,则可以更改对接口A中的方法的所有调用以使用对ImplA方法的直接调用,而无需执行任何虚拟方法。一旦加载了另一个类AnotherImplA,就不再可能进行这种优化。卸载AnotherImplA后,可能会再次出现。

JVM中有许多使用运行时收集的数据的优化。预先做好所有优化会错失很多机会。

另请查看Cliff Click最近的演讲,A JVM Does That?

答案 3 :(得分:1)

他所说的是在运行时将所有字节码编译为机器语言是不切实际的。您可以预先编译所有内容,但这不是JIT采用的方法。

首先,不知道程序有多大。人们会在30分钟的启动时感到非常沮丧,因为它编译了它可以找到的每个库(给定的Java程序不在单个文件中,它可以访问类路径中的所有内容)

另一方面,即使您告诉系统您的程序将使用哪些组件,也无法确定您的程序可以用于给定的运行 - 人们会在30分钟的启动运行时感到更加不安命令行程序,参数由“--help”

组成

最后,它可以通过在运行时进行编译来做一些很好的技巧。使用这样的方法:

public testMeh(int param) {
    if(param == 35) 
        do bunches of crap;
    else if (param > 5)
        do much more crap;
    else 
        return;
    }

编译器可以调用它一次或两次,并且即时识别值5和刚刚返回。如果一直调用值为2,它可以用if(param!= 2)testMeh(param)替换ENTIRE方法调用;

取消了该号码的整个方法调用。后来它可以发现,不调用该方法意味着某些成员变量无法更改,并且可能会将代码的其他部分折叠为空。

如果您预先编译内容,这很简单。我的意思是,当你识别模式时,你可以在任何地方编写异常代码,但你的代码很快就会变成一场噩梦。

现在,如果你问为什么不在编译成字节码时预先编译整个程序 - 这是一个不同的问题,而不是引用的解决方案 - 但你可以这样做。它已经完成并且运行良好。您可以提高可移植性和运行时灵活性,从而缩短启动时间。

答案 4 :(得分:1)

启动时间更快,并且能够在执行期间分析代码后进行更智能的优化。

本文为这个问题提供了一个很好的答案:https://advancedweb.hu/2016/05/27/jvm_jit_optimization_techniques/

JVM开始即时解释所有内容。它跟踪函数被调用的次数,如果它超过某个限制,它将编译该函数并缓存此机器代码。每次调用该函数后,它都会执行此机器代码。如果它通过另一个阈值,它可以再次编译,但执行更积极的优化。

目标是平衡编译的前期成本。预先编译所有内容将导致更长的启动时间,并且编译器无法在不看代码运行的情况下进行最佳优化。与较短的运行程序相比,运行时间较长的程序具有更好的优化潜力。

如果循环执行了很多次,它也可以只编译循环体。

相关问题