百科问答小站 logo
百科问答小站 font logo



常说「Java 在虚拟机中运行」,请问这个虚拟机可以视为 Java 语言的解释器吗? 第1页

  

user avatar   zhao-ce-33 网友的相关建议: 
      

这里有三种执行方式,解释执行,即时编译(just in time),超前编译(ahead of time)

我们知道,机器能够执行的,是机器码,native code,也就是01组成的那些东西,但是我们写出来的源码,都是一些英语单词以及符号的拼凑,不管是什么编程语言。我们写的是英语单词以及符号的源代码,但是机器能够执行的是机器码

那要让机器执行我们写好的源代码,就需要把源代码翻译成机器能够执行的机器码,然后交给机器执行

这里有两个步骤,第一步翻译,也就是编译,第二步执行

所谓的解释执行,就是边编译,边执行,这个叫做解释器,interpreter,大部分脚本语言,都是解释执行,比如javascript,python,ruby,perl,groovy这些

还有一种,为了执行的时候速度快一点,就在编写源码之后,将源代码编译成机器码,这就是编译compilation,这样做之后,在执行的时候,因为已经编译好了,所以执行起来,性能就比较好,就快,传统上c等语言,都是这种方式

那后者在执行的时候,性能更好,但是多了一步,编译,前者因为编译和执行是在一个过程里面,所以执行的时候,需要先编译再执行,所以性能就差,所以脚本语言一般性能都比较差,所以多数时候,脚本都是用在一些性能不敏感的领域,比如web,而前者,则多用在一些性能敏感的领域,尤其是对latency延迟比较敏感的领域,比如游戏领域,还有苹果的应用商店,为了客户的体验,同时也为了打击热更新,所以原则上会要求开发者将app编译成native方能上架

然后我们来说java,你可能注意到了,我们一开始列举了三种编译方式,但是我们前面只说了两种,解释执行以及编译成机器码

那这里我们要说一下,编译成机器码的一个问题,那就是不同平台上用的机器码是不一样的,你在windows上用的机器码,就不能在macos以及linux上执行,如果同样的源代码,你想在另外一个平台上执行的话,那么你得到目标平台上,再做一次编译,比如你在windows上写好了源代码,但是你想把你写好的源代码放到macos上去执行,那么对不起,你需要再去找一台macos,然后在macos上编译之后,才能执行。当然有一种说法叫做交叉编译,就你可以在win上编译出mac上能够执行的程序,但多数时候,这个都难以令人满意,就你压根找不到交叉编译的工具,没有人去做这事

那为了写一个源码而不需要到处编译,所以java就提出了一个jit的概念,那就是,在编写完源代码之后,编译成一个字节码,bytecode,这个字节码,是介于源代码和机器码之间的一种过渡性质的东西,然后java会针对目标平台,提供不同的虚拟机,比如win上就提供win上的虚拟机,mac上就提供mac上的虚拟机,这个虚拟机在执行的时候,会将字节码,翻译成机器码,并最终执行

这样就把整个程序啊,变成跨平台和不跨平台的两个部分,不跨平台的部分就是java的虚拟机,跨平台的部分就是字节码(class文件,或者将class文件根据某种格式压缩打包的jar,jar其实就是zip,换个后缀而已)

那这样做的好处就是,一次编译,到处运行。我只需要编译出字节码,然后交给目标平台上的虚拟机去执行就可以了,我更新程序,并不需要再跑到不同的目标平台也就是操作系统上去编译,就方便了很多,而且编译成机器码的过程很慢,编译成字节码要快很多,java的编译速度是很快的,这个如果你有大型c或者c++项目的经验就知道了,编译成机器码很慢

同时,java的虚拟机,还会记录翻译字节码为机器码的过程,并做出优化处理,所以在运行一定时间之后,java的执行效率就会逼近甚至超过直接执行机器码的效率。为什么会超过呢?因为它会根据编译的过程提供的一些信息,做出一些的优化

那为了区别这两者,java的这种先编译成字节码,然后再交给虚拟机执行的过程,叫做jit,just in time compilation,即时编译,为了以示区分,把直接编译成机器码,再执行的过程,叫做aot,ahead of time compliation,超前编译

那这里说一下java提供的编译器,openjdk里面有jvm,也就是虚拟机,这个虚拟机的名字叫做hotspot,然后openjdk里面的编译器,叫做c1或c2,一般用后者,前者是针对客户端设计的

那现在java世界,有一个新的编译器,叫做graal[1],graal可以提供跟c2同样的功能,同时它也能提供aot编译器,也就是graal可以把java的源代码编译成机器码,而不仅仅是字节码

graal是用java写的,而openjdk是用c++写的

所以graal是用java实现了java源码的编译,这个就是编译器的自举

那jit和aot各有优劣,一个直观的对比如下:

可以看出来,aot在启动速度,内存使用,以及编译后的包的规模上,都有明显优势,而jit则在吞吐和减少最大延迟上,有优势,因为jit可以根据编译过程,自动做出优化,所以jit启动比较慢,然后执行的效率,需要在一定时间之后,才能逼近甚至超过机器码的执行效率,这个过程叫做warmup,热身

所以虚拟机是不是java的解释器,严格说起来,并不是,但是它的即时编译的模式,跟多数脚本所使用的解释执行,有一定的相似之处

一般认为,jit即时编译,是结合了解释执行以及aot超前编译两者优点的产物,当然这个并不完美,有代价,需要做取舍,如果你非常在乎启动时间,内存使用的大小或者编译后产物的大小的话,你可能还是倾向于用aot超前编译,而不是jit即时编译

当然我个人觉得,能够让用户自由选择jit还是aot,才是最吼的,就像java这样,谷歌的flutter/dart也做到了同时支持jit和aot,一般建议开发debug时候用jit,release发布时候再用aot处理。苹果的xcode将来应该也会支持jit,但是目前swift这些主要还是用aot,因为毕竟jit在debug时候,在编译速度上,优势明显

最后,java现在也支持直接执行.java文件,现在openjdk也可以解释执行了,这个强化是jep 330的东西[2],另外jdk现在也提供了jshell,也就是java的repl,所以大部分脚本的功能,其实jdk也都有了

所以现在的java,其实是同时支持:解释执行,即时编译(jit)以及 超前编译(aot)三种模式,用户可以根据需要,选择自己想用的方式,做或者不做编译,以及编译成什么东西

参考

  1. ^ https://www.graalvm.org/
  2. ^ https://www.infoq.com/articles/single-file-execution-java11/



  

相关话题

  编程中,有哪些好的习惯从一开始就值得坚持? 
  如何评价阿里近期发布的Java编码规范? 
  java的引用明明和指针没什么本质区别,java为什么还宣称没有指针并把这个当作语言的优点? 
  Rust 语言现在什么情况,为什么知乎上不怎么讨论Rust语言了呢? 
  前端开发的难点到底在什么地方? 
  Java 有哪些好的设计? 
  Java中,有一个for循环调用网络api很耗时,请问如何减少耗时? 
  C++ 是否适合做 GUI? 
  为什么我国的计算机科技领域发展了十几年水平依旧落后国外这么多? 
  jdk9为何要将String的底层实现由char[]改成了byte[]? 

前一个讨论
这是什么东西缠在了蚯蚓身上,像是在猎食?
下一个讨论
外星人发现地球文明所需要的科技水平有多高?





© 2024-05-20 - tinynew.org. All Rights Reserved.
© 2024-05-20 - tinynew.org. 保留所有权利