编译技术大约分为两种,一种AOT,只线下(offline)就将源代码编译成目标机器码,这是普遍用在系统程序语言中;另一种是JIT,只及时的编译,但是大部分的JIT引擎,针对的是将IR(中间代码,如JavaByteCode) 在运行时, 有针对性的翻译成机器码。

对于JIT我们必须认识到,JIT不是简单的讲字节码翻译成机器码。一个JIT引擎要想快,主要取决于:1. 通过搜集runtime信息,识别出程序的热点(2/8原则)2. 最重要的一点,根据信息实时优化技术的使用。相反,JIT并不仅仅是个字节码到机器码的直译机器。

那么楼主疑问,为何不直接全部翻译成机器码了?要回答这个问题,必须要提到以下两个观点:1.JIT是重型优化,本身overhead很大. 2. 最重要的,现代JIT技术必须依赖runtime信息,但要得到runtime信息,程序必须执行.下面稍微解释一下.

1. JIT,毫无疑问是重型优化技术,如果程序没有循环,并且只执行一次,那么你JIT它,估计只会让程序执行更慢,因为在用户看来,程序运行时间=程序实际的运行时间+JIT的时间.其实,如果你的程序非常简单,JVM是不会做任何的JIT的。

2.据我所知,Orcale HotSpot是使用的tracing jit,这是当前非常热门的技术。JIT技术,大体上分为:1 Per-Method-A-Time,即每次JIT一个函数,函数是最小的JIT单元,2 Tracing JIT,即每次JIT一个热点流程,基本上你可以理解为你程序中的热循环。Tracing JIT需要程序先执行,然后再执行的过程中分析出哪个是潜在的热循环,在针对它进行JIT;而Per-Method-A-Time,则同样需要得到runtime信息,在针对热点方法(如一个方法被调用100次)进行JIT。以HotSpot为例,他要实施tracing jit,首先它得有个基本的(baseline) 解释器/虚拟机 或者是naive的机器码去执行程序,在执行过程中,JVM得搜集信息(比如记录循环的计数器)识别出热的循环/代码path,然后JVM还要记录下这个热循环所执行的所有字节码(叫做tracelet);接着JIT引擎才开始进行优化(基本就是转换成SSA,然后各种执行优化pass),最后生成的机器码再kick in,JVM再次执行到这个部分的时候,他就会调用相关的JIT的机器码了。当然了,Java是强类型语言,所以很多动态语言的类型check就省去了。

如果JVM直接编译字节码到机器码,实际上它就不是JIT,它属于AOT。举个例子,据我所知,ActionScript是AOT编译的,后端貌似是LLVM,虽然它看起来像那种会JIT的“脚本”。

最后在提个题外话,JIT vs AOT是个很古老的论战了,到底谁是最好的编译方法?我无能回答。在我看来:JIT是一个充满希望的方向,因为它可以搜集到程序在AOT编译时得不到的runtime数据,在优化时,有更多的上下文可以依靠,理论上应该有更好的优化特性;而AOT则因为在线下,所以本身的编译速度并不关键,所以他也,理论上,可以付诸实施发更为复杂的优化算法。但是,就目前而言,短期内AOT依然是胜利者,我们必须认识到一个问题,即使JIT可以搜集到宝贵的运行时信息,但是程序毕竟在运行,他不可能肆无忌惮的记录下大量的信息和数据,这样JIT本身就会成为程序运行时一个非常昂贵的开销,无论是内存还是CPU时间,再加上优化算法一个个的pass也有开销,所以JIT,虽然处在一个美好的地位,但是他依然很难完全的发挥它的优势。当然了,如果JIT有了更革命的突破,他依然是美好的明天吧!