Java是一种编译型还是解释型编程语言?

230

过去我使用C++作为编程语言。 我知道用C++编写的代码需要通过编译过程,才能成为目标文件 "机器码"。

我想了解Java在这方面的工作原理。 计算机如何运行用户编写的Java代码?


18
C++ 可以被解释。目前有一些 C 解释器可供使用。 - Tom Hawtin - tackline
9个回答

281

Java的实现通常使用两步编译过程。Java源代码由Java编译器编译成字节码。JVM执行字节码。现代JVM使用一种称为即时编译(JIT)的技术,在运行时动态地将字节码编译成硬件CPU可理解的本机指令。

JVM的一些实现可能选择解释字节码而不是JIT编译它到机器码并直接运行。虽然这仍然被认为是“解释器”,但它与读取和执行高级源代码的解释器(即在这种情况下,Java源代码不直接解释,而是Java编译器的输出字节码)非常不同。

从技术上讲,将Java编译为原生代码并运行生成的二进制文件是可能的。也可以直接解释Java代码。

总之,根据执行环境,字节码可以是:

  • 提前编译并作为本机代码执行(类似于大多数C++编译器)
  • JIT编译并执行
  • 解释
  • 通过支持的处理器直接执行(字节码是某些CPU的本机指令集)

25
实际上,一些HotSpot JVM在开始时会解释字节码,只有在确定哪些值得编译并收集了一些关于代码运行方式的统计数据后,才将它们编译成本地代码;例如,为了找出每个条件分支中最常见的路径。 - Stephen C
2
因此有了“热点”这个术语 :) 它经常对正在运行的内容进行优化,以获得最佳效果。 - Noon Silk
4
你可以通过在HotSpot中使用-Xcomp关闭解释器。值得尝试在应用程序上看看这是一个多么糟糕的想法。 - Tom Hawtin - tackline
1
有这样一句话:“Sun HotSpot JVM 的当前版本使用一种称为即时 (JIT) 编译的技术,在运行时动态地将字节码编译成 CPU 可理解的本机指令。” 我原本以为 JVM 是一个解释器,但它似乎进一步编译了字节码。我感到困惑。此外,它还写道它是在运行时动态完成的。有人能解释一下吗? - Anand

127

在此输入图片描述

Java编写的代码:

  • 首先通过名为javac的程序编译成字节码,如上图左侧所示;
  • 然后,如上图右侧所示,另一个名为java的程序启动Java运行时环境,并且它可以使用Java解释器/JIT编译器来编译和/或解释字节码。

Java何时解释字节码,何时编译字节码?应用程序代码最初被解释,但JVM监视频繁执行的字节码序列并将其转换为机器码以直接在硬件上执行。对于仅执行几次的字节码,这样可以节省编译时间并减少初始延迟;对于频繁执行的字节码,则使用JIT编译以高速运行,在缓慢解释的初始阶段后。此外,由于程序大部分时间都在执行少数代码,因此减少的编译时间非常重要。最后,在初始代码解释期间,可以在编译之前收集执行统计信息,这有助于执行更好的优化。


Java使用了很多内存是因为缓存的字节码吗? - Pedro Gordo
3
“很多内存”这个表述不够明确。Java的内存管理相当简单- JVM使用三代来创建和维护对象。此另一个SO答案可能对您有用。 - displayName
通过上述解释,理论上,C++编译的代码应该始终比逻辑上类似的Java代码快,因为总会有某些部分的.class文件被JIT决定不转换为机器代码。换句话说,Java永远无法达到C++所展示出的裸金属执行速度。这个假设是正确的吗? - DevdattaK
@DevdattaK:我不太懂C++,但我的猜测是,对于较小和专业化的程序,Java可能会更快地给出结果,因为它不会浪费时间编译那些代码部分,在那里没有太多的加速可用。 - displayName
1
@DevdattaK,你的假设在这个维基页面中有讨论 https://en.m.wikipedia.org/wiki/Java_performance?wprov=sfla1简而言之,这并不总是正确的。 - Sundar Rajan
1
@PedroGordo - 其实不是这样。真正的原因是Java使用垃圾回收机制来回收分配在GC堆中的所有对象。为了高效运行,垃圾收集器需要大量(额外的)内存,这是一种权衡。你可以更频繁地运行GC并使用更少的空间,但是你会花费更多的CPU周期来运行GC。而且由于运行GC的成本与非垃圾数量成比例...你会失去更多。持有字节码的内存只占典型Java应用程序总内存使用量的一小部分。 - Stephen C

76
"解释型语言"或"编译型语言"这些术语并没有意义,因为任何编程语言都可以被解释和/或编译。
至于Java的现有实现,大多数都涉及到将代码编译成字节码,所以它们需要编译。运行时还可以动态加载字节码,因此总是需要某种形式的字节码解释器。该解释器可能会或可能不会在内部使用编译成本地代码。
如今,许多曾经被认为是“解释性”的语言都使用了部分即时编译,例如JavaScript。"

9
此外,谷歌的V8 JavaScript执行引擎不仅进行部分即时编译。事实上,它总是编译成本机代码,V8甚至没有解释器。它只有编译器(类似于Maxine,但与Maxine不同,V8只有一个编译器)。这三个例子(GCJ、Maxine和V8)更加强调了你的观点:并不存在所谓的解释语言或编译语言。一种语言既不是解释的也不是编译的。一种语言只是存在着(这实际上是Shriram Krishnamurthi的名言)。 - Jörg W Mittag
5
你为什么在一个Java问题中谈论JavaScript? - Koray Tugay
2
@KorayTugay 仅仅是举个例子。我当然不想暗示 Java 和 Javascript 除了名字的前四个字母以外有任何共同之处。 - starblue
难道解释语言和编译语言之间的差异不意味着编译语言二进制文件不能在任何时候更改其执行流程,而解释语言则非常服从某些当前函数的工作方式吗?在C中,库是可选的,而在其他语言中,您无法拥有一个数组对象,而没有一个可以更新或在另一个平台上完全不同的C二进制扩展。脚本语言将能够在两者上运行,而编译语言需要不同的二进制文件才能运行。 - Eaton Emmerich

56

Java代码会被编译为字节码,然后进入Java虚拟机进行解释执行。


39
...但并非严格准确。 - Stephen C
4
JVM可能选择不对字节码进行“解释”。它可以即时编译并直接执行它。 - Mehrdad Afshari
1
JIT并不直接执行代码,它只是记忆如何执行代码的方式。 - cletus
7
在 JIT 编译完成后,会直接执行代码。JIT 会读取一段字节码(例如一个完整的方法),将其编译为机器码并跳转到执行。 - Mehrdad Afshari
@KorayTugay 编译后的代码不会自动运行,除非有“某些东西”跳转到它。虚拟机将通过简单地跳转到第一条指令并让 CPU 处理其余部分来执行刚 JIT 编译的本机代码。 - Mehrdad Afshari
显示剩余2条评论

19

Java是一种编译型编程语言,不过它并不会直接编译成可执行的机器码,而是编译成一个叫JVM字节码的中间二进制格式。这个字节码会被编译和/或解释来运行程序。


14

有点同时都是。首先,Java被编译(有些人更喜欢说“翻译”)为字节码,然后根据JIT的心情,它可能会被编译或解释。


41
那是一款先进的软件,可以开发出情绪 :) - Thorarin
7
JIT确实是一款非常复杂的软件,它可以根据运行时信息(比如分析器)进行优化,而预编译的编译器无法做到这一点(因为它没有关于程序运行时行为的信息)。但它可能并不真的有情绪... :-) - Jesper

11

Java既进行编译又进行解释。

在Java中,程序不会被编译成可执行文件。它们被编译成字节码(如前所述),然后由JVM(Java虚拟机)在运行时进行解释/执行。当我们使用javac编译器时,Java源代码会被编译成字节码。 字节码会以.class文件扩展名的形式保存在磁盘上

当程序要运行时,使用即时(JIT)编译器可以将字节码转换为机器码。结果是将机器码馈送到内存并执行。

Javac是Java编译器,它将Java代码编译为字节码。 JVM是Java虚拟机,它将字节码转换为本地机器代码并运行/解释/翻译字节码。尽管Java被认为是一种解释型语言,但当字节码在JVM中时,它可以使用JIT(即时)编译。 JIT编译器动态读取字节码的许多部分(或全面,很少情况下),将其编译为机器代码,使程序能够更快地运行,然后缓存并在之后重复使用而无需重新编译。因此,JIT编译结合了编译代码的速度和解释性的灵活性。

解释型语言是一种编程语言,其大多数实现直接自由地执行指令,而不必事先将程序编译为机器语言指令。解释器会直接执行程序,将每个语句翻译为一个或多个已编译为机器码的子程序序列。

编译型语言是一种编程语言,其实现通常是编译器(从源代码生成机器代码的转换器),而不是解释器(按步骤执行源代码的执行器,在此过程中没有进行预运行时翻译)

在像Java这样的现代编程语言实现中,为平台提供两种选项变得越来越流行。


应该是“字节码可能被转换”,而不是“转换”。Java规范定义了字节码。无论这个字节码是(a)直接在硬件上运行,(b)通过解释器运行,(c)预先编译,还是(d)在运行时部分即时编译,都留作实现细节。请注意,各种真实的Java实现确实使用了这四种选项中的所有四种。 - Basil Bourque
谢谢您指出这一点。如果字节码没有转换为机器码会发生什么?我可以想象一种情况,即字节码是某些处理器的本地指令集,那么就不需要进行转换。或者我有什么遗漏吗? - prime
点击我给出的Jazelle DBX(Direct Bytecode eXecution) 技术链接,其中JVM字节码的一个子集是CPU本地机器指令(有点像)。没有这个,你会得到从字节码生成的机器码(a)从解释器(即时),(b)预编译,或(c)使用即时编译器实时生成并缓存的代码。 - Basil Bourque

-3

引用来源: https://blogs.oracle.com/ask-arun/entry/run_your_java_applications_faster

应用程序开发人员可以在今天市场上提供的各种操作系统中开发应用程序代码。 Java语言在这个阶段对操作系统是不可知的。 Java应用程序开发人员编写的出色源代码现在被编译为Java字节码,这在Java术语中被称为客户端编译。 Java字节码的编译使Java开发人员能够“一次编写”。 Java字节码可以在任何兼容的操作系统和服务器上运行,因此使源代码不依赖于操作系统/服务器。创建Java字节码后,Java应用程序与底层操作系统/服务器之间的交互更加密切。旅程继续-企业应用程序框架在称为Java虚拟机(JVM)或Java运行时环境(JRE)的运行时环境中执行这些Java字节码。 JVM与底层操作系统和硬件有密切联系,因为它利用操作系统和服务器提供的资源。 Java字节码现在被编译为平台特定的机器语言可执行代码。这被称为服务器端编译。

因此我会说Java肯定是一种编译语言。


-3

Java是一种字节编译语言,针对一个名为Java虚拟机的基于堆栈的平台进行编译,并在许多平台上具有一些非常快速的实现。


1
"byte-compiled" 是什么意思? - Jesper
2
@ Jesper:通常,“Byte-compiled” 意味着“编译成字节码”。 “Bytecode” 是一个通用术语,涵盖任何类型的非文本中间代码(通常不是机器可执行的)。 - Greg Hewgill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接