Java“虚拟机”与Python“解释器”的说法?

2022-08-31 05:44:37

在Java中,似乎很少读到Python的“虚拟机”,而在Java中,“虚拟机”一直在使用。

两者都解释字节码;为什么一个叫虚拟机,另一个叫解释器?


答案 1

在这篇文章中,“虚拟机”指的是进程虚拟机,而不是像Qemu或Virtualbox这样的系统虚拟机。过程虚拟机只是一个程序,它提供了一个通用的编程环境 - 一个可以编程的程序。

Java既有解释器又有虚拟机,Python有虚拟机和解释器。“虚拟机”是Java中更常见的术语,而“解释器”是Python中更常见的术语,这与两种语言之间的主要区别有很大关系:静态类型(Java)与动态类型(Python)。在此上下文中,“类型”是指基元数据类型 - 表示数据的内存中存储大小的类型。Java虚拟机很容易。它要求程序员指定每个变量的基元数据类型。这为 Java 字节码提供了足够的信息,不仅可以由 Java 虚拟机解释和执行,甚至可以编译成机器指令。Python虚拟机更复杂,因为它承担了在执行每个操作之前暂停的额外任务,以确定操作中涉及的每个变量或数据结构的基元数据类型。Python将程序员从原始数据类型的思考中解放出来,并允许在更高层次上表达操作。这种自由的代价是性能。“解释器”是Python的首选术语,因为它必须暂停才能检查数据类型,还因为动态类型语言的相对简洁的语法非常适合交互式界面。构建交互式Java接口没有技术障碍,但是尝试以交互方式编写任何静态类型的代码将是乏味的,因此它不是以这种方式完成的。

在Java世界中,虚拟机抢走了这个节目,因为它运行用一种语言编写的程序,这种语言实际上可以编译成机器指令,结果是速度和资源效率。Java字节码可以由Java虚拟机执行,相对而言,性能接近编译程序。这是由于字节码中存在基元数据类型信息。Java虚拟机将Java放在自己的类别中:

可移植解释型静态类型语言

下一个最接近的是LLVM,但LLVM在不同的级别运行:

可移植解释型汇编语言

术语“字节码”在Java和Python中使用,但并非所有字节码都是平等的。字节码只是编译器/解释器使用的中间语言的通用术语。即使是像gcc这样的C编译器也使用一种(或几种)中间语言来完成工作。Java 字节码包含有关基元数据类型的信息,而 Python 字节码不包含。在这方面,Python(以及Bash,Perl,Ruby等)虚拟机确实比Java虚拟机慢得多,或者更确切地说,它只是有更多的工作要做。考虑哪些信息以不同的字节码格式包含是很有用的:

  • llvm: cpu 寄存器
  • Java:基元数据类型
  • Python:用户定义的类型

举一个现实世界的类比:LLVM处理原子,Java虚拟机处理分子,Python虚拟机处理材料。由于一切最终都必须分解成亚原子粒子(真实机器操作),因此Python虚拟机具有最复杂的任务。

静态类型语言的Intepreters/编译器与动态类型语言的解释器/编译器没有相同的包袱。静态类型语言的程序员必须承担这一不足,为此,回报是性能。但是,正如所有非确定性函数都是秘密确定性的一样,所有动态类型语言都是秘密静态类型的。因此,两个语言系列之间的性能差异应该在Python将其名称更改为HAL 9000时趋于平稳。

像Python这样的动态语言的虚拟机实现了一些理想化的逻辑机器,并且不一定非常接近任何真正的物理硬件。相比之下,Java虚拟机在功能上与经典的C编译器更相似,除了它不是发出机器指令,而是执行内置的例程。在Python中,整数是一个Python对象,它附加了一堆属性和方法。在Java中,int是指定的位数,通常为32。这并不是一个公平的比较。Python整数确实应该与Java Integer类进行比较。Java的“int”基元数据类型无法与Python语言中的任何东西进行比较,因为Python语言只是缺少这一层基元,Python字节码也是如此。

因为Java变量是显式类型的,所以人们可以合理地期望像Jython这样的性能与cPython处于同一水平。另一方面,用Python实现的Java虚拟机几乎可以保证比泥浆慢。不要指望Ruby,Perl等会变得更好。他们不是为做到这一点而设计的。它们是为“脚本”而设计的,这就是动态语言编程的名称。

在虚拟机中发生的每个操作最终都必须命中真实的硬件。虚拟机包含预编译的例程,这些例程足够通用,可以执行逻辑操作的任意组合。虚拟机可能不会发出新的机器指令,但它肯定会以复杂的序列一遍又一遍地执行自己的例程。Java虚拟机,Python虚拟机和所有其他通用虚拟机都是平等的,因为它们可以被诱导执行您可以想到的任何逻辑,但是它们在执行哪些任务以及留给程序员的任务方面是不同的。

Psyco for Python不是一个完整的Python虚拟机,而是一个即时编译器,它劫持了常规Python虚拟机,它认为它可以编译几行代码 - 主要是循环,它认为某些变量的原始类型将保持不变,即使值随着每次迭代而变化。在这种情况下,它可以放弃常规虚拟机的一些持续类型确定。不过,你必须小心一点,以免你把类型从Psyco的脚下拉出来。但是,Pysco通常知道,如果它不完全相信类型不会改变,就回退到常规虚拟机。

这个故事的寓意是,原始数据类型信息对编译器/虚拟机确实很有帮助。

最后,从这个角度来看,请考虑一下:由Python解释器/虚拟机执行的Python程序,该程序在Java中实现,在LLVM中实现的Java解释器/虚拟机上运行,在iPhone上运行的qemu虚拟机中运行。

永久链接


答案 2

虚拟机是一个虚拟计算环境,具有一组特定的原子明确定义的指令,这些指令独立于任何特定语言受支持,并且通常被认为是一个沙箱本身。VM 类似于特定 CPU 的指令集,并且倾向于在更基本的级别上工作,具有独立于下一个指令(或字节代码)的非常基本的构建块。指令仅基于虚拟机的当前状态确定性地执行,而不依赖于该时间点指令流中其他位置的信息。

另一方面,解释器更复杂,因为它被定制为解析特定语言的某些语法流和必须在周围标记的上下文中解码的特定语法的流。你不能孤立地查看每个字节甚至每一行,并确切地知道下一步该做什么。语言中的令牌不能像相对于 VM 的指令(字节码)那样孤立地获取。

Java 编译器将 Java 语言转换为字节码流,这与 C 编译器将 C 语言程序转换为汇编代码没有什么不同。另一方面,解释器并没有真正将程序转换为任何明确定义的中间形式,它只是将程序操作作为解释源代码的过程。

VM和解释器之间差异的另一个测试是你是否认为它是独立于语言的。我们所知道的Java VM并不是真正特定于Java的。您可以从其他语言创建编译器,这些编译器会产生可以在JVM上运行的字节码。另一方面,我不认为我们真的会想到将Python以外的其他语言“编译”成Python,以便Python解释器进行解释。

由于解释过程的复杂程度,这可能是一个相对缓慢的过程。专门解析和识别语言令牌等,并了解源的上下文,以便能够在解释器内执行执行过程。为了帮助加速这种解释型语言,我们可以在这里定义预解析、预标记化源代码的中间形式,这些源代码更容易直接解释。这种二进制形式仍然在执行时被解释,它只是从一个可读性低得多的人类可读形式开始,以提高性能。但是,执行该表单的逻辑不是虚拟机,因为这些代码仍然不能孤立地进行 - 周围令牌的上下文仍然很重要,它们现在只是以不同的计算机效率更高的形式存在。