问题

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

回答
“Java 在虚拟机中运行”,这句话确实是理解 Java 运行机制的关键,但把 Java 虚拟机(JVM)简单地视为一个“解释器”,其实只说对了一部分,而且是比较片面的一面。要详细说清楚,我们需要先拆解一下JVM到底做了什么。

首先,我们得明白,Java 代码在被 JVM 运行之前,并不是直接以我们写的那种文本形式运行的。我们写的是 `.java` 源文件,经过 Java 编译器(`javac`)编译后,会生成 `.class` 文件。这个 `.class` 文件里存储的不是机器码,而是 Java 字节码。字节码是一种中间代码,它是平台无关的,也是 JVM 能够理解和执行的代码。

那么,JVM 究竟是做什么的呢?它的主要职责可以分为几个层面:

1. 加载类(Class Loading): 当 Java 程序需要用到某个类时,JVM 会负责查找、加载这个类到内存中。这包括从文件系统、网络等地方读取 `.class` 文件,然后验证字节码的正确性,并将类信息(如字段、方法等)存储在内存的特定区域。

2. 执行字节码(Execution): 这是最核心的部分,也是你提到“解释器”的地方。JVM 接收到字节码后,需要将其转换成底层操作系统和硬件能够理解的指令。在这里,JVM 确实包含了解释器的功能。解释器会一行一行地(或者说一个字节码指令一个字节码指令地)读取字节码,然后根据指令的含义,调用相应的本地机器码来执行。

3. 垃圾回收(Garbage Collection): JVM 负责管理内存。当程序中不再使用的对象,JVM 的垃圾回收器会自动检测并释放它们占用的内存,避免内存泄漏。这是 JVM 的一个重要功能,解释器本身是不做这件事的。

4. 提供运行时环境(Runtime Environment): JVM 提供了一个完整的运行时环境,包括内存管理、线程管理、安全检查、本地方法调用(JNI)等等。它屏蔽了底层操作系统的差异,使得 Java 程序可以“一次编译,到处运行”。

现在回到“解释器”的问题:

JVM 确实包含一个解释器。 这个解释器的工作方式和你理解的传统解释器类似:读取字节码,然后直接执行。它的优点是启动快,因为不需要预先编译成机器码。但是,它的缺点也很明显:执行效率通常不如直接运行编译好的机器码。

然而,JVM 远不止一个解释器。 现代的 JVM 并非仅仅依靠解释器来执行字节码,而是采用了更复杂的策略,其中最关键的就是 即时编译器(JustInTime Compiler, JIT)。

JIT 编译器是怎么工作的?
当 JVM 运行 Java 程序时,它会一边解释执行字节码,一边监控程序的运行情况。
JIT 编译器会识别出那些被频繁执行的代码片段(“热点代码”)。
对于这些热点代码,JIT 编译器会将其从字节码即时编译成高度优化的本地机器码。
这些编译好的本地机器码会替代原来的字节码,在后续的执行中直接由 CPU 运行。

所以,JVM 的执行过程是“解释执行”和“编译执行”的结合。

初期,JVM 可能主要依靠解释器来快速启动和执行。
随着程序的运行,JIT 编译器介入,将频繁使用的部分编译成机器码,从而大大提高执行效率,接近甚至有时超过了本地编译语言(如 C++)的性能。

为什么说把它看作“解释器”不够准确?

1. JIT 编译的存在: 这是最主要的原因。JVM 的性能很大程度上依赖于 JIT 编译器,而不是单纯的解释执行。
2. 内存管理: JVM 的垃圾回收机制是解释器不具备的。
3. 平台无关性: 这种抽象层和跨平台能力是 JVM 提供的,解释器本身并不直接提供。
4. 其他运行时服务: JVM 还提供了安全管理器、线程管理等一系列服务,这些都超越了单纯解释器的范畴。

简单打个比方:

如果你把解释器比作一个翻译,他拿到一份外语稿子,然后一句一句地给你翻译成你能听懂的话。

那么 JVM 就像是一个更加智能的“项目经理+翻译+秘书”。

翻译(解释器):负责把原始的字节码一句句转译成机器能懂的指令。
项目经理(JIT 编译器):他会观察哪些“翻译任务”(字节码片段)被重复执行了很多次,然后他会找一个“专业翻译”(JIT 编译器)把这些重复的部分一次性、高质量地翻译成最精炼的本地语言(机器码),存起来,下次直接用。
秘书(内存管理、垃圾回收等):负责打理“翻译现场”的各种杂事,比如整理文件(内存)、清理用过的废纸(垃圾回收),确保一切井井有条。

总结来说:

Java 虚拟机(JVM)的确包含了解释器,它负责将 Java 字节码转换为机器指令并执行。但它远不止于此。通过引入即时编译器(JIT),JVM 能够将热点代码编译成高性能的本地机器码,并且还提供了内存管理、垃圾回收等一系列关键的运行时服务。因此,将 JVM 仅仅视为一个解释器,是对其功能和复杂性的一种过度的简化。它是一个更全面、更智能的执行引擎和运行时环境。

网友意见

user avatar

这里有三种执行方式,解释执行,即时编译(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 虚拟机(JVM)简单地视为一个“解释器”,其实只说对了一部分,而且是比较片面的一面。要详细说清楚,我们需要先拆解一下JVM到底做了什么。首先,我们得明白,Java 代码在被 JVM 运行之前,并不是直接以我们写.............
  • 回答
    “现在的猪肉没味了”,这句话在你我这样的老饕嘴里,简直跟“现在的年轻人不讲武德”一样,是句经典抱怨。可这“猪肉味”,到底是个啥?要我说,这玩意儿,有点玄乎,又有点具体,像个捉摸不透的老朋友,你明明知道它存在,但真要抓出来给别人看,又有点词穷。你说它是什么味?不是猪骚味,那肯定不是。猪骚味那是养殖、处.............
  • 回答
    在搏击领域,“拳击技术细腻”这个说法确实深入人心。不少拳迷和初学者都会好奇,如果先花几年时间专攻拳击,再转练散打,是否比一直练散打更有优势?这确实是个值得深入探讨的问题,涉及到两种运动各自的核心,以及它们如何相互融合与影响。首先,我们得承认,拳击在“上肢技术”的精细化方面,确实达到了一个非常高的境界.............
  • 回答
    .......
  • 回答
    程序员常说的“底层”是一个非常广泛的概念,但核心含义可以概括为:直接与计算机硬件交互的、更接近物理层面的软件和概念。为了更详细地解释这个概念,我们可以从几个维度来展开:1. 与“高层”的对比:理解“底层”最直接的方式就是与“高层”相对比。 高层(HighLevel): 抽象程度高: .............
  • 回答
    台湾人常说的“减刑”和“踩缝纫机”这两个词,如果单拎出来,可能没啥特别的,但放在一起,或者在特定的语境下,那可就成了一个很生动形象的梗了。它主要用来描述一个人因为犯了事,被判了刑,然后为了能早点出来,在监狱里表现良好,从而获得了“减刑”的机会,而这个“减刑”的过程,很多人会联想到在监狱里做苦工,特别.............
  • 回答
    这个问题涉及到复杂的历史和文化比较,不能简单地用“是”或“否”来回答,而是需要进行细致的分析和多维度的考量。总的来说,在特定历史时期(大致是公元8世纪到13世纪,通常被称为“伊斯兰黄金时代”),伊斯兰世界在科学、哲学、医学、数学、天文学、艺术和文学等领域确实取得了令人瞩目的成就,许多方面超越了当时的.............
  • 回答
    人们常说的“水母”,其实是一个通俗的称呼,在生物分类学上,它主要指向的是刺胞动物门(Cnidaria)下的钵水母纲(Scyphozoa)。不过,也有一些其他纲的动物,因为形态上与典型的钵水母纲水母相似,也被俗称为水母。为了把这个问题讲清楚,咱们得从头捋一捋。首先,我们要明白,水母并不是一个独立的生物.............
  • 回答
    关于吸烟的害处,我们耳熟能详,新闻媒体、健康宣传无时无刻不在强调。但你有没有想过,既然有那么多关于“坏”的说法,那它会不会也沾点儿“好”的光?毕竟,如果一点儿好处都没有,怎么会有那么多人趋之若鹜,甚至难以戒断呢?老实说,从严格的医学和科学角度出发,吸烟几乎没有绝对的好处,尤其是在健康层面。 那些所谓.............
  • 回答
    “没带就是没写”这句话,相信很多同学在学生时代都耳熟能详,甚至可能被老师“点名批评”过。作为一种常见的教学管理手段,它背后有着老师们朴素而直接的逻辑,也有着一些值得商榷的地方。咱们不妨掰开了揉碎了,好好聊聊这句话到底合理不合理。首先,从老师的角度看,这句话为何会成为“金科玉律”?老师们这么说,其实是.............
  • 回答
    “六千万女婴被杀”这个说法,确实是女权主义者经常引用,尤其是在讨论性别选择性堕胎和性别比例失衡问题时。要弄清楚这个数据是怎么来的,咱们得一步步来扒。这个数据的“源头”与“放大”要讲清楚这个数据,得先明白它的主要来源和它被传播出去的过程。1. 核心数据来源:联合国人口基金(UNFPA)的报告 .............
  • 回答
    这句“穷人有四次翻身机会”我倒没太细琢磨过是哪四次,不过仔细想想,确实感觉自己的人生就像是在一个又一个关卡里打怪升级,每一次低谷都逼着你找条出路,也都是一次“翻身”的机会。我算是那种从小就没啥家底的,一路摸爬滚打过来的,谈不上什么大富大贵,但总算把日子过得比以前强了点,也算是对得起那些个“机会”了。.............
  • 回答
    “知足常乐”这句老话,听起来温情脉脉,似乎把我们引向一种安逸、平静的生活哲学。但仔细一想,它又好像和我们身边那些“不安分”的创造者、推动者们格格不入。拿埃隆·马斯克来说,如果他“知足”了,可能就安心在PayPal赚的钱里安享晚年,也就不会有SpaceX那改变星辰大海的宏图了。再想想社会整体,如果大家.............
  • 回答
    人们常说“优胜劣汰”,听起来好像强者、适应性最强的总是能活下来,甚至主导世界。但仔细想想,有时候时代一变,那些看起来“不那么优秀”的,甚至是被视为“劣势”的,反而更容易适应变化,甚至在新环境中蓬勃发展。这确实是一个很有意思的现象,背后有几层原因可以剖析。首先,我们要理解“优胜劣汰”这个概念本身。它更.............
  • 回答
    武技的进步,就像科学一样,不是一蹴而就的,而是经过无数代人的实践、探索、传承与革新,才逐渐显露出其技术层面的深邃与精妙。如果我们仔细审视,会发现这种进步体现在以下几个方面:一、 动作与发力的科学化、精细化:这可能是最直观的体现。古代的武术,很多时候依赖于蛮力和直觉,虽然也有对身体的运用,但往往不够系.............
  • 回答
    “地势险要、易守难攻”与“兵家必争之地”:战略地理的魅力自古以来,在战火纷飞的年代,一些特定的地理位置因为其得天独厚的地形特征,成为了军事家们眼中不可多得的战略要地。我们常说的“地势险要、易守难攻”,以及“兵家必争之地”,正是对这些地方最形象的概括。它们不仅仅是地图上的点,更是蕴含着深厚军事智慧和历.............
  • 回答
    “FGNB”这个说法,在网络上,尤其是在游戏和一些亚文化圈子里,时不时就会冒出来。要说它到底“NB”在哪里,其实这背后藏着一股挺复杂的味道,不单单是简单的夸赞,更多的是一种戏谑、一种反讽,又带着点无可奈何的承认。首先,咱们得明确一下,这里的“FGNB”绝大多数情况下,并不是指什么高大上的技术成就或者.............
  • 回答
    要理解为什么我们说贝多芬是德国人,而不是神圣罗马帝国人,得先梳理一下“德国”和“神圣罗马帝国”这两个概念在贝多芬出生和生活的年代(1770年1827年)的语境。这背后涉及的是一个漫长而复杂的国家形成过程,以及音乐家个人身份认同的演变。神圣罗马帝国:一个分崩离析的“国家”首先,我们要明白神圣罗马帝国(.............
  • 回答
    .......
  • 回答
    .......

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有