Answers:
JIT编译器会在执行之前或什至已经执行时即时对其进行编译。这样,运行代码的VM可以检查代码执行中的模式,以允许仅使用运行时信息才能进行的优化。此外,如果VM出于某种原因(例如,太多的高速缓存未命中或代码频繁抛出特定异常)而决定编译版本的性能不够好,则它可能会决定以不同的方式重新编译它,从而导致更智能汇编。
另一方面,C和C ++编译器传统上不是JIT。它们只能在开发人员的计算机上一次编译一次,然后生成可执行文件。
JIT是即时编译器的缩写,名称是misson:在运行时,它确定了有价值的代码优化并加以应用。它不能代替普通的编译器,而是解释器的一部分。请注意,使用中间代码的Java之类的语言都具有:用于将源代码转换为中间代码的普通编译器,以及用于提高性能的解释器中包含的JIT。
代码优化当然可以由“经典”编译器执行,但要注意主要区别:JIT编译器可以在运行时访问数据。这是一个巨大的优势。显然,正确利用它可能很难。
例如,考虑如下代码:
m(a : String, b : String, k : Int) {
val c : Int;
switch (k) {
case 0 : { c = 7; break; }
...
case 17 : { c = complicatedMethod(k, a+b); break; }
}
return a.length + b.length - c + 2*k;
}
普通的编译器对此不能做太多。但是,JIT编译器可能检测到m
仅k==0
出于某种原因而被调用(类似的事情可能会随着代码随时间的变化而发生)。然后,它可以创建代码的较小版本(并将其编译为本机代码,尽管从概念上讲我认为这是次要的点):
m(a : String, b : String) {
return a.length + b.length - 7;
}
在这一点上,它可能甚至会内联方法调用,因为它现在很简单。
显然,Sun放弃javac
了Java 6中曾经做过的大多数优化。有人告诉我,这些优化使JIT很难做很多事情,而且天真地编译的代码最终运行得更快。去搞清楚。
m
了版本,没有检查k
,因为这将是无法证明,m
就永远不会有一个非零被称为k
,但即使没有能够证明它可以取代它与static miss_count; if (k==0) return a.length+b.length-7; else if (miss_count++ < 16) { ... unoptimized code for
m ...} else { ... consider other optimizations...}
。
k=0
始终(这不是输入或环境的函数)才可以安全地放弃测试-但是确定测试需要静态分析,因为静态分析的成本很高,因此只能在编译时负担得起。当通过代码块中的一条路径使用的频率比其他路径高得多时,JIT可以获胜,而专门用于此路径的代码版本将更快。但是JIT仍然会发出测试,以检查快速路径是否适用,如果不是,则采用“慢速路径”。
Base* p
参数,并通过它调用虚函数;运行时分析表明,指向始终(或几乎始终)的实际对象似乎是Derived1
类型。JIT可以使用对Derived1
方法的静态解析(甚至内联)调用来生成函数的新版本。此代码之前有一个条件,该条件检查p
的vtable指针是否指向期望的Derived1
表;如果不是,它会以较慢的动态解析方法调用跳转到该函数的原始版本。