JAVA平台的理解
主题: JAVA是解释执行还是编译执行?
我的答案 : 混合模式
闲谈 :
1. JAVA(write once,run anywhere);
2. GC(Garbagae Collection),Java通过垃圾收集器回收分配内存,大部分情况下,程序猿不需要自己操心内存的分配和回收。
3. JRE,也就是java运行环境,包含了JVM和JAVA类库,以及一些模块等。而JDK可以看做是JRE的一个超集,提供了更多工具,比如编译器、
各种诊断工具等。
步入主题:
对于“Java是解释执行”这句话,这个说法不太准确。 我们看流程, 我们开发的JAVA源代码,首先通过Javac编译成为字节码(bytecode),然后,在运行时,通过
JAVA虚拟机(JVM)内嵌的解释器将字节码转换成为最终的机器码。(但是常见的JVM,比如我们大多数情况使用的Oracle JDK提供的Hotspot JVM,都提供了JIT(Just-In-
Time)编译器,也就是通过所说的动态编译器,JIT能够在运行时将热点代码编译成机器码,这种情况下部分热点代码就属于编译执行了,而不是解释执行了。)
这里,我们来研究2点,从上面的话可以看出,基本上比较普遍的情况就是解释执行,但是JIT有时也能出现,这就是避免了主要重复的业务代码暂居大部分开销的情况。
第二个点是:什么是 编译执行,解释执行。
编译型语言:在程序运行之前,有一个单独的编译过程,将程序翻译成机器语言,以后执行这个程序时,就不用再进行翻译了。
解释型语言:是在运行的时候将程序翻译成机器语言,所以运行速度相对于编译型语言要慢。
SO,二者之间最大的区别就在于是否存下目标机器码:编译会把输入的源程序以某种单位(例如基本块/函数/方法/trace等)翻译生成目标机器码,并存下来(无论是在磁盘上或是内存中)后续执行可以复用;解释则是把源程序中的指令逐条解释执行,边解释边执行,不存下目标代码,后续执行没有可以复用的信息。
了解Java的运行过程:Java源文件(*.java),通过java编译器(javac)编译生成一个ByteCode字节码文件(*.class),字节码由java自己设计的一个计算机(即java虚拟机,JVM)解释执行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的目标机器码,然后在特定的机器上运行。
所以说Java的解释器的优点是比较容易让用户实现自己跨平台的代码,同一套代码可以在几乎所有的操作系统上执行。
(这里提一下java的跨平台Write Once,Run Anywhere:java的跨平台是基于JVM的。JVM是在一台计算机上由软件或是硬件模拟的计算机,java程序所有的*.class文件都是在JVM上运行的,也就是说*.class文件只需认JVM,由JVM去适应各个操作系统,所以不同的操作系统只要安装符合其类型的JVM,那么程序无论到哪个OS上都是可以正确执行的JVM for Unix / JVM for Windows / JVM for Other……,因此没有JVM也就不能跨平台)
虽然Java的第一道工序是javac编译,其目标文件是ByteCode,而并非机器语言,但后续可能有三种处理方式:
1、运行时,ByteCode由JVM逐条送给解释器,解释器将翻译成机器码运行。
2、运行时,部分ByteCode可能由实时编译器(Just In Time Compiler,JIT)编译为目标机器码再执行(以method为翻译单位,还会保存起来,第二次执行就不用再翻译为机器码了),因为考虑到有些JVM是采用纯JIT编译方式实现的,其内部没有解释器,例如:JRockit、Maxine VM。
3、RTSJ,继javac之后执行AOT二次编译,生成静态的目标平台码。
有的时候,可能是以上三种方式同时在使用,至少,1和2是同时使用的,3则需要程序员手工指定。
补充 :
1. JDK 8 实际上是解释和编译混合的一种模式,即所谓的混合模式(-Xmixed)。通过运行在server模式的JVM,会进行上万次调用以收集足够的信息进行高效的编译,
client模式这个门限是1500次。oracle HotSpot JVM 内置了2个不同的JIT compiler,C1对应前面说的client模式,适用于对于启动速度敏感的应用,比如java桌面应用。
C2对应server模式,他的优化是为长时间运行的服务器端应用设计的。默认是采用所谓的分层编译(TieredCompilation)。
2. Java虚拟机启动是,可以指定不同的参数对运行模式进行选择。比如,指定“-Xint”,就是告诉JVM只进行解释执行,不对代码进行编译,这种模式抛弃了JIT可能带来的
性能优势。毕竟解释器(interpreter)是逐条读入,逐条解释运行的。与其对应的,还有一个“-Xcomp”参数,这就是告诉JVM关闭解释器,不要进行解释执行,或者叫做
最大优化级别。(但是这种模式并不意味着最高效)。xcomp会导致JVM启动变慢非常多,同时有些JIT编译器优化方式,比如分支预测,如果不进行profiling,
往往并不能进行有效优化。
3. AOT(Ahead-of-Time Compilation),直接将字节码编译成机器代码。这样就避免了JIT预热等各方面的开销,比如Oacle JDK 9 就引入了实验性的AOT特性,并且增加了
新的 jaotc 工具。利用下面的命令把某个类或者某个模块编译成为AOT库。
jaotc --output libHelloWorld.so HelloWorld.class jaotc --output libjava.base.so --module java.base
然后,在启动时直接指定就可以了。
java -XX:AOTLibarary=./libHelloWorld.so,./libjava.base.so HelloWorld
而且, oracle jdk 支持分层编译和AOT协作使用,这2者并不是二选一的关系。可参考: http://openjdk.java.net/jeps/295
4. 同时,类似JVM这种跨平台特性,就像JAVA当中“解耦”这种艺术手法一样,在Scale,Groovy,Jruby等语言上,也得到了体现。