详解Java的编译执行与解释执行

一、前言

编程语言分为低级语言和高级语言,机器语言、汇编语言是低级语言,C、C++、java、python等是高级语言。
机器语言是最底层的语言,能够直接执行。而我们编写的源代码是人类语言,计算机只能识别某些特定的二进制指令,在程序真正运行之前必须将源代码转换成二进制指令。汇编语言通过汇编器翻译成机器指令后执行,一条汇编指令,对应着一条机器指令。

高级语言编程的程序有三种执行方式:

1.一种是编译执行,源程序先通过编译器(负责将源程序翻译成目标机器指令)翻译成机器指令,通过编译-->链接-->目标可执行文件,然后执行;即提前将所有源代码一次性转换成二进制指令,也就是生成一个可执行程序。比如C,C++等语言都是编译执行的。

2.一种是解释执行,是使用解释器会将我们的一句句代码解释成机器可以识别的二进制代码来执行,可以认为是,解释一句,执行一句。在这个过程中,不会生成中间文件。如:脚本方式是一条条命令,在执行时,是由系统的解释器,将其一条条翻译成机器可识别的指令,例如shell脚本是由shell程序执行的,js是由浏览器解释执行的。

3.最后一种是编译和解释相结合的执行方式,下面我们来说Java。

二、理解Java的几个编译器

前端编译器:把.java文件转变成.class文件。包括Sun的Javac、Eclipse JDT中的增量式编辑器(ECJ)

后端运行期即时编译器(JIT编译器,Just In Time Compiler):把字节码转成机器码。包括HotSpot VM的C1、C2编译器

静态提前编译器(AOT编译器,Ahead Of Time Compiler):把*.java编译成本地机器码。包括GNU Compiler for the Java(GCJ)、Excelsior JET

三、Java采用的是解释和编译混合的模式

在编译时期,我们通过将源代码编译成.class ,配合JVM这种跨平台的抽象,屏蔽了底层计算机操作系统和硬件的区别,实现了“一次编译,到处运行” 。 而在运行时期,目前主流的JVM 都是混合模式(-Xmixed),即解释运行 和编译运行配合使用。

Java一开始被定位为“解释执行”的语言,但是现在主流的虚拟机中都包含了即时编译器JIT。

程序从源代码到运行经历阶段:java程序--(编译javac)-->字节码文件.class-->类装载子系统化身为反射类Class--->运行时数据区--->(解释执行+JIT编译器编译)-->操作系统(Win,Linux,Mac JVM)。

.class文件就是可以到处运行的文件。然后Java字节码会被转化为目标机器代码,这是是由JVM来执行的,即Java的第二次编译。

Java采用的是解释和编译混合的模式:基于JVM执行引擎当中的解释器interpreter与即使编译器JIT共存

执行引擎获取到,由javac将源码编译成字节码文件class.

然后在运行的时候通过解释器interpreter转换成最终的机器码。(解释型)

另外JVM平台支持一种叫作即时编译的技术。即时编译的目的是避免函数被解释执行,而是将整个函数体编译成为机器码,这种方式可以使执行效率大幅度提升(直接编译型)

JIT将字节码转换成最终的机器码

以 Oracle JDK提供的HotSpot虚拟机为例,在HotSpot虚拟机中,提供了两种编译模式:解释执行 和 即时编译(JIT,Just-In-Time)。

解释执行即逐条翻译字节码为可运行的机器码,而即时编译则以方法为单位将字节码翻译成机器码(上述提到的“编译执行”)。前者的优势在于不用等待,后者则在实际运行当中效率更高。

即时编译存在的意义在于它是提高程序性能的重要手段之一。根据“二八定律”(即:百分之二十的代码占据百分之八十的系统资源),对于大部分不常用的代码,我们无需耗时间将之编译为机器码,而是采用解释执行的方式,用到就去逐条解释运行;对于一些仅占据小部分的热点代码(可认为是反复执行的重要代码),则可将之翻译为符合机器的机器码高效执行,提高程序的效率,此为运行时的即时编译。

为了满足不同的场景,HotSpot虚拟机内置了多个即时编译器:C1,C2与Graal。Graal 是Java10正式引入的实验性即时编译器,在此暂不叙述(其实我不是很了解,尴尬···)。先看一下C1、C2 ,相信大家或多或少接触过。

  • C1:即Client编译器,面向对启动性能有要求的客户端GUI程序,采用的优化手段比较简单,因此编译的时间较短。
  • C2:即Server编译器,面向对性能峰值有要求的服务端程序,采用的优化手段复杂,因此编译时间长,但是在运行过程中性能更好。

从Java7开始,HotSpot虚拟机默认采用分层编译的方式:热点方法首先被C1编译器编译,而后热点方法中的热点再进一步被C2编译,根据前面的运行计算出更优的编译优化。为了不干扰程序的正常运行,JIT编译时放在额外的线程中执行的,HotSpot根据实际CPU的资源,以 1:2的比例分配给C1和C2线程数。在计算机资源充足的情况,字节码的解释运行和编译运行时可以同时进行,JIT编译执行完后的机器码会在下次调用该方法时启动,已替换原本的解释执行(意思就是已经翻译出效率更高的机器码,自然替换原来的相对低效率执行的方法)。

以上,可以看出在Java中不单单是解释执行,即时编译(编译执行)在Java性能优化中彰显重要的作用,所以现在应该说:Java是解释执行和编译执行共同存在的,至少大部分是这样。

四、编译与解释比较

1.一段程序编译会浪费时间,并且移植到其他平台上时还要进行重新编译,但是其编译后生成的可执行文件运行速度快。

2.解释型程序可跨平台执行,无需将全部代码编译之后再运行,能够及时运行,但因为是逐条解释执行所以最终的运行速度不如编译型程序。

3.内存使用:编译执行需要生成编译后的机器码文件,而解释执行时逐句解释执行,所以解释执行对内存占用更少。

单独使用解释器的缺点

抛弃了JIT可能带来的性能优势。如果代码没有被JIT编译的话,再次运行时需要重复解析。

单独使用JIT编译器的缺点

需要将全部的代码编译成本地机器码。要花更多的时间,JVM启动会变慢非常多;

增加可执行代码的长度(字节码比JIT编译后的机器码小很多),这将导致页面调度,从而降低程序的速度。

有些JIT编译器的优化方式,比如分支预测,如果不进行profiling,往往并不能进行有效优化。

因此,HotSpot采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系统资源的只有那一小部分的代码(热点代码),而这也就是JIT所需要编译的部分。JVM会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。

JDK 9引入了一种新的编译模式AOT(Ahead of Time Compilation),它是直接将字节码编译成机器码,这样就避免了JIT预热等各方面的开销。JDK支持分层编译和AOT协作使用。

注:JIT为方法级,它会缓存编译过的字节码在CodeCache中,而不需要被重复解释。

以上就是详解Java的编译执行与解释执行的详细内容,更多关于Java的资料请关注我们其它相关文章!

(0)

相关推荐

  • java编译后的文件出现xx$1.class的原因及解决方式

    java编译后的文件名字带有$接数字的就是匿名内部类的编译结果,接名字的就是内部类的编译结果 例如: TestFrame$1.class是匿名内部类的编译结果,TestFrame$MyJob.class则是内部类MyJob编译后得到的. 使用内部类可以隐藏一些实现的细节, 等等, 还有其他一些好处. 使用匿名类的时候, 要注意代码的可读性 补充知识:JNI之javah使用时报错:找不到类文件 初学java,想使用JNI,在用javah生成头文件时,总是报错找不到类: 看了javah的help,本

  • Java中关于Null的9个解释(Java Null详解)

    对于Java程序员来说,null是令人头痛的东西.时常会受到空指针异常(NPE)的骚扰.连Java的发明者都承认这是他的一项巨大失误.Java为什么要保留null呢?null出现有一段时间了,并且我认为Java发明者知道null与它解决的问题相比带来了更多的麻烦,但是null仍然陪伴着Java. 我越发感到惊奇,因为java的设计原理是为了简化事情,那就是为什么没有浪费时间在指针.操作符重载.多继承实现的原因,null却与此正好相反.好吧,我真的不知道这个问题的答案,我知道的是不管null被Ja

  • java配置变量的解释,搬运他人优质评论(推荐)

    第一种: 在配置环境变量中: 设置JAVA_HOME: 一是为了方便引用,比如,JDK安装在C:\jdk1.6.0目录里,则设置JAVA_HOME为该目录路径, 那么以后要使用这个路径的时候, 只需输入%JAVA_HOME%即可, 避免每次引用都输入很长的路径串; 二则是归一原则, 当JDK路径改变的时候, 仅需更改JAVA_HOME的变量值即可, 否则,就要更改任何用绝对路径引用JDK目录的文档, 要是万一没有改全, 某个程序找不到JDK, 后果是可想而知的----系统崩溃! 三则是第三方软件

  • Java中类赋值的解释实例详解

    Java中类赋值的解释实例详解 Java是面向对象的存储语言,进行的是信息的传递,也就是类的赋值,实际上他们占用的是同样的存储空间: 下面上一个自己写的例子: 感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

  • Java编译和解释执行对比及原理解析

    编程语言分为低级语言和高级语言,机器语言.汇编语言是低级语言,C.C++.java.python等是高级语言. 机器语言是最底层的语言,能够直接执行.而我们编写的源代码是人类语言, 计算机只能识别某些特定的二进制指令,在程序真正运行之前必须将源代码转换成二进制指令. 汇编语言通过汇编器翻译成机器指令后执行,一条汇编指令,对应着一条机器指令. 高级语言编程的程序有三种执行方式: 1.一种是编译执行,源程序先通过编译器(负责将源程序翻译成目标机器指令)翻译成机器指令,通过编译-->链接-->目标可

  • 改善Java代码之慎用java动态编译

    动态编译一直是Java的梦想,从Java 6版本它开始支持动态编译了,可以在运行期直接编译.java文件,执行.class,并且能够获得相关的输入输出,甚至还能监听相关的事件.不过,我们最期望的还是给定一段代码,直接编译,然后运行,也就是空中编译执行(on-the-fly),来看如下代码: public class Client { public static void main(String[] args) throws Exception { //Java源代码 String sourceS

  • Java设计模式之解释器模式(Interpreter模式)介绍

    Interpreter定义:定义语言的文法,并且建立一个解释器来解释该语言中的句子. Interpreter似乎使用面不是很广,它描述了一个语言解释器是如何构成的,在实际应用中,我们可能很少去构造一个语言的文法.我们还是来简单的了解一下. 首先要建立一个接口,用来描述共同的操作. 复制代码 代码如下: public interface AbstractExpression { void interpret( Context context );     } 再看看包含解释器之外的一些全局信息 复

  • JAVA设计模式之解释器模式详解

    在阎宏博士的<JAVA与模式>一书中开头是这样描述解释器(Interpreter)模式的: 解释器模式是类的行为模式.给定一个语言之后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器.客户端可以使用这个解释器来解释这个语言中的句子. 解释器模式的结构 下面就以一个示意性的系统为例,讨论解释器模式的结构.系统的结构图如下所示: 模式所涉及的角色如下所示: (1)抽象表达式(Expression)角色:声明一个所有的具体表达式角色都需要实现的抽象接口.这个接口主要是一个interpre

  • java编译命令基础知识点

    我们在对计算机下达指令时,人类的语言它是不能够明白,需要通过编译的时候翻译成计算机能听懂的语言.编译过程中会调用javac命令,这点大家可能接触的不多,毕竟是是计算机程序内部运行时的操作.下面我们就编译的概念.命令带来讲解,然后分享一个编译实例给大家练习. 1.编译概念 通过流程图可以看出其实java的执行可以分为两大步骤,第一是编译,这一过程就是调用的javac命令,编译成对应的.class文件.第二是解释执行,这一过程是调用的java命令. 2.编译命令 (1)linux rm -rf Ma

  • java编译器和JVM的区别

    Java虚拟机(JVM)是可运行Java代码的假想计算机.只要根据JVM规格描述将解释器移植到特定的计算机上,就能保证经过编译的任何Java代码能够在该系统上运行.java编译器把java编译成字节码,也就是.class文件,然后JVM给编译成的字节码提供运行环境.java的源代码是无法直接在JVM上运行的. 1.java编译器 Java语言写的源程序通过Java编译器,编译成与平台无关的'字节码程序'(.class文件,也就是0,1二进制程序),然后在OS之上的Java解释器中解释执行. 也相

  • java利用JEXL实现动态表达式编译

    背景 做项目突然遇到这样的需求: 系统要获取多个数据源的数据,并进行处理,最后输出多个字段.字段的计算规则一般是简单的取值最多加一点条件判断. 而且需要动态变动!!例如一个字段a的取值,如果a > 10的时候输出10,a <= 10则输出a.这里的10可能在一天后改成8,也可能在后天就改成了12.当然,如果只是一个数字的变动还好说,我们可以使用数据库进行存储.但是,万一哪天需求突然变成了a < 10的时候输出10,a >=10 则输出a,就需要对代码改动,再测试再发布才能到生产环境

  • java实现动态编译并动态加载

    在D盘test目录下有个java文件:AlTest.java public class AlTest { public String sayHello(){ System.out.println("AlTest类 sayHello()方法正在执行...."); return "hello word"; } } 现需要实现在工程已经运行过程中,进行java文件到class文件的编译操作,并运行AlTest类的方法 package com.piao.job; impor

随机推荐