Java虚拟机;vs. Python“解释器”;的说法吗?

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

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

75388 次浏览

他们之间没有真正的区别,人们只是遵循创造者选择的惯例。

术语不同的一个原因可能是,人们通常认为向python解释器提供人类可读的原始源代码,而不用担心字节码之类的问题。

在Java中,必须显式地编译为字节码,然后在VM上只运行字节码,而不是源代码。

尽管Python在幕后使用虚拟机,但从用户的角度来看,大多数时候可以忽略这个细节。

术语解释器是一个遗留术语,可以追溯到早期的shell脚本语言。由于“脚本语言”已经演变成功能齐全的语言,它们对应的平台也变得更加复杂和沙箱化,虚拟机和解释器(在Python意义上)之间的区别非常小,甚至不存在。

Python解释器仍然以与shell脚本相同的方式运行,从某种意义上说,它可以在不需要单独的编译步骤的情况下执行。除此之外,Python解释器(或Perl或Ruby的)和Java虚拟机之间的区别主要是实现细节。(有人可能会说Java比Python更加完全沙箱化,但两者最终都通过原生C接口提供对底层架构的访问。)

不要忘记Python为x86提供了JIT编译器,这进一步混淆了问题。(见psyco)。

对“解释型语言”的更严格的解释只有在讨论VM的性能问题时才有用,例如,与Python相比,Ruby被认为更慢,因为它是一种解释型语言,不像Python——换句话说,上下文就是一切。

虚拟机是一种虚拟计算环境,具有一组特定的、定义良好的原子指令,这些指令的支持不依赖于任何特定的语言,它通常被认为是一个沙盒。VM类似于特定CPU的指令集,并且倾向于在更基本的级别上使用这些指令(或字节码)的非常基本的构建块,这些构建块是独立于下一个指令的。一条指令只根据虚拟机的当前状态确定地执行,而不依赖于当时指令流中其他地方的信息。

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

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

VM和解释器区别的另一个测试是你是否认为它是独立于语言的。我们所知道的Java虚拟机并不是Java特有的。您可以使用其他语言制作编译器,生成可以在JVM上运行的字节代码。另一方面,我不认为我们真的会考虑将Python以外的其他语言“编译”为Python以供Python解释器解释。

由于解释过程的复杂性,这可能是一个相对缓慢的过程....具体来说,解析和识别语言标记等,并理解源的上下文,以便能够在解释器中进行执行过程。为了帮助加速这种解释型语言,我们可以在这里定义预解析、预标记化源代码的中间形式,这些源代码更容易直接解释。这种二进制形式仍然在执行时进行解释,它只是从一种不太适合人类阅读的形式开始,以提高性能。然而,执行该形式的逻辑并不是虚拟机,因为这些代码仍然不能被孤立地看待——周围标记的上下文仍然很重要,它们现在只是以一种不同的、更有效的计算机形式存在。

翻译,将源代码转换为一些有效的中间表示(代码),并立即执行。

虚拟机,显式执行编译器构建的存储预编译代码,编译器是解释器系统的一部分。

虚拟机的一个非常重要的特征是,在虚拟机内部运行的软件,受限于虚拟机提供的资源。确切地说,它无法跳出它的虚拟世界。想想远程代码的安全执行,Java applet。

在python的情况下,如果我们保留佩克文件,如本文评论中提到的,那么机制将变得更像一个虚拟机,并且这个字节码执行得更快——它仍然会被解释,但从一个更计算机友好的形式。如果我们从整体上看,PVM是Python解释器的最后一步。

底线是,当我们提到Python解释器时,这意味着我们将其作为一个整体来引用,而当我们说PVM时,这意味着我们只是在谈论Python解释器的一部分,一个运行时环境。类似于Java,我们引用不同的部分,JRE, JVM, JDK等。

要了解更多信息,请参阅Wikipedia条目:翻译虚拟机。还有另一个在这里。在这里你可以找到应用虚拟机比较。它有助于理解编译器,解释器和vm之间的区别。

在本文中,“虚拟机”指的是进程虚拟机,而不是 系统虚拟机,如Qemu或Virtualbox。进程虚拟机为 一个简单的程序,它提供了一个通用的编程环境——程序 这是可以编程的。< / p > Java有一个解释器和一个虚拟机,Python有一个虚拟机 机器和一个解释器。之所以说“虚拟机”是一个比较多的 而“解释器”是Java中比较常见的术语,在Python中有很多 这与两种语言的主要区别有关:静态类型 (Java) vs动态类型(Python)。在这里,“类型”指的是 基本数据类型——表示内存中存储大小的类型 数据。Java虚拟机很容易做到。它要求程序员 指定每个变量的基本数据类型。这就提供了 对象解释和执行Java字节码的信息 Java虚拟机,但即使是编译成机器指令。 Python虚拟机更复杂,因为它采用 在执行每个操作之前暂停以确定的附加任务 类中涉及的每个变量或数据结构的基本数据类型 操作。Python使程序员从原始数据的角度思考问题 类型,并允许在更高级别上表示操作。的价格 这种自由就是表现。“解释器”是Python的首选术语 因为它必须暂停以检查数据类型,而且还因为 相对简洁的动态类型语言的语法非常适合 交互接口。制作互动游戏不存在技术障碍 Java接口,但试图交互式地编写任何静态类型的代码 会很乏味,所以不是那样做的。

在Java世界中,虚拟机是最引人注目的,因为它运行程序 用一种可以编译成机器指令的语言编写, 结果就是速度和资源效率。可以执行Java字节码 通过Java虚拟机,性能接近编译 程序,相对来说。这是由于原始数据的存在 在字节码中键入信息。Java虚拟机将Java放在 自身类别:

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

仅次于LLVM的是LLVM,但LLVM在不同的级别上运行:

可移植解释汇编语言

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

  • llvm: cpu寄存器
  • Java:基元数据类型
  • Python:用户定义类型
在现实世界中做一个类比:LLVM使用原子,即Java虚拟机 Python虚拟机处理的是材料。 因为所有东西最终都必须分解成亚原子粒子(真实的 机器操作),Python虚拟机有最复杂的任务 静态类型语言的解释器/编译器只是没有相同的 动态类型语言的解释器/编译器的包袱。 静态类型语言的程序员必须承担起这一重任 回报就是表现。然而,就像所有的不确定函数一样 秘密地确定,所有动态类型语言都秘密地确定 静态类型。两个语系之间的表现差异 因此,在Python将其名称更改为HAL 9000时,应该趋于平稳 动态语言(如Python)的虚拟机实现了一些理想化的 逻辑机器,并没有必要非常紧密地对应任何现实 物理硬件。相比之下,Java虚拟机在 一个经典的C编译器的功能,除了它不是发射 机器指令,它执行内置的例程。在Python中,整数是 带有一堆属性和方法的Python对象。在 在Java中,int是指定的位数,通常是32。这并不是 公平的比较。Python的整数应该与Java的整数进行比较 整数类。Java的“int”基本数据类型不能与 Python语言,因为Python语言缺少这一层 基本类型,Python字节码也是如此 因为Java变量是显式类型的,人们可以合理地期望 像Jython这样的性能在同一范围内 cPython的。另一方面,一个用Python实现的Java虚拟机 肯定比泥的速度要慢。不要指望Ruby, Perl等等, 为了更好的生活。它们的设计初衷不是这样的。它们是为

.“脚本”,这是动态语言编程的称呼

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

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

.

. 这个故事的寓意是原始数据类型信息是真正的 对编译器/虚拟机有帮助 最后,考虑一下这个:一个执行的Python程序 由Python解释器/运行在Java上的Java虚拟机实现 解释器/虚拟机在LLVM中实现,运行在qemu虚拟中

permalink

首先,你应该明白,编程或计算机科学一般不是数学,我们经常使用的大多数术语都没有严格的定义。

现在回答你的问题:

什么是解释器(计算机科学)

它按最小的可执行单元翻译源代码,然后执行该单元。

什么是虚拟机

对于JVM来说,虚拟机是一个包含解释器、类加载器、垃圾收集器、线程调度器、JIT编译器和许多其他东西的软件。

正如你所看到的,解释器是JVM的一部分,整个JVM不能被称为解释器,因为它包含许多其他组件。

为什么在谈论python时要用“解释器”这个词

对于Java,编译部分是显式的。 另一方面,Python的编译和解释过程不像Java那样明确,从最终用户的角度来看,解释是用于执行Python程序的唯一机制

不,它们并不都解释字节码。

Python只在运行pypy时解释字节码。否则,它将被编译成C并在该级别上进行解释。

Java编译为字节码。

为了对“为什么是Java虚拟机,而不是Python解释器?”问题提供一个深入的答案,让我们尝试回到编译理论领域,作为讨论的出发点。

程序编译的典型过程包括以下步骤:

  1. 词法分析。将程序文本分割为有意义的“词”,称为令牌(作为过程的一部分,所有注释、空格、换行等都被删除,因为它们不影响程序行为)。结果是一个有序的令牌流。
  2. 语法分析。从令牌流构建所谓的抽象语法树(AST)。AST建立了令牌之间的关系,从而定义了程序求值的顺序。
  3. 语义分析。使用有关类型的信息和编程语言的一组语义规则来验证AST的语义正确性。(例如,从句法的角度来看,a = b + c是正确的语句,但如果a被声明为常量对象,从语义的角度来看,这是完全错误的)
  4. 中间代码生成。将AST序列化为与机器无关的“原语”操作的线性有序流。事实上,代码生成器遍历AST并记录求值步骤的顺序。结果,从程序的树状表示中,我们得到了更简单的列表状表示,其中程序求值的顺序得到了保留。
  5. 机器代码生成。程序以独立于机器的“原始”字节码的形式被转换成特定处理器架构的机器代码。

好的。现在我们来定义这些术语。

翻译,在这个词的经典含义中,基于直接从程序文本产生的基于AST的程序求值假设执行。在这种情况下,程序以源代码的形式分发,解释器由程序文本提供,通常以动态方式(一条语句接一条语句或一行接一行)。对于每个输入语句,解释器构建它的AST并立即计算它,改变程序的“状态”。这是脚本语言的典型行为。例如Bash, Windows CMD等。从概念上讲,Python也采用这种方式。

如果我们将基于ast的执行步骤替换为在解释器中生成与机器无关的中间二进制字节码步骤,我们将把整个程序执行过程分成两个独立的阶段:编译和执行。在这种情况下,以前的解释器将变成字节码编译器,它将程序从文本的形式转换为某种二进制形式。然后,程序以二进制形式分发,而不是以源代码的形式分发。在用户机器上,该字节码被送入一个新的实体——虚拟机,该实体实际上解释了该字节码。因此,虚拟机也被称为字节码解释器。但请注意这里!经典解释器是文本翻译,但虚拟机是二进制翻译!这是Java和c#采用的方法。

最后,如果我们将机器代码生成添加到字节码编译器中,我们将实现所谓的经典编译器。经典编译器将程序源代码转换为特定处理器的机器代码。然后,该机器代码可以是目标处理器上的直接执行,而不需要任何额外的中介(不需要任何类型的解释器,无论是文本解释器还是二进制解释器)。

现在让我们回到最初的问题,考虑Java和Python。

Java最初被设计为具有尽可能少的实现依赖。它的设计基于“一次编写,随处运行”(WORA)的原则。为了实现它,Java最初被设计为一种编程语言,它可以编译成与机器无关的二进制字节码,然后可以在所有支持Java的平台上执行,而不需要重新编译。你可以像考虑基于wora的c++那样考虑Java。实际上,Java更接近c++,而不是像Python这样的脚本语言。但与c++不同的是,Java被设计为编译成二进制字节码,然后在Java2的环境中执行,而c++被设计为在机器码中编译,然后由目标处理器直接执行。

Python最初被设计为一种脚本编程语言,它解释脚本(按照编程语言规则编写的文本形式的程序)。因此,Python最初支持对单行命令或语句的动态解释,就像Bash或Windows CMD那样。出于同样的原因,Python的初始实现没有任何类型的字节码编译器和虚拟机来执行这些字节码,但从一开始Python就需要翻译,它能够理解和计算Python程序文本

因此,在历史上,Java开发人员倾向于谈论Java虚拟机(因为最初,Java已经作为Java字节码编译器和字节码解释器——JVM的包出现),而Python开发人员倾向于谈论Python解释器(因为最初Python没有任何虚拟机,是一种经典的文本翻译,直接执行程序Java虚拟机0,而不需要任何形式的编译或转换为任何形式的二进制代码)。

目前,Python在底层也有虚拟机,可以编译和解释Python字节码。而这一事实又增加了“为什么是Java虚拟机,而不是Python解释器?”的困惑,因为这两种语言的实现似乎都包含虚拟机。 但是!即使在当前,对程序文本的解释也是Python程序执行的主要方式。Python实现将虚拟机作为一种优化技术来利用。在虚拟机中解释二进制字节码要比直接解释原始程序文本高效得多。同时,对于Python语言设计者和Python程序开发者来说,虚拟机在Python中的存在是绝对透明的。同一种语言可以在有虚拟机和没有虚拟机的解释器中实现。以同样的方式,相同的程序可以在有虚拟机和没有虚拟机的解释器中执行,这些程序将展示完全相同的行为,并从相同的输入产生相同的输出。唯一可观察到的区别是程序执行的速度和解释器消耗的内存量。因此,Python中的虚拟机并不是语言设计中不可避免的一部分,而只是主要Python解释器的可选扩展

可以用类似的方式来考虑Java。Java内部有一个JIT编译器,可以有选择地将Java类的方法编译成目标平台的机器码,然后直接执行。但是!Java仍然使用字节码解释作为Java程序执行的主要方式。就像Python实现专门利用虚拟机作为优化技术一样,Java虚拟机使用即时编译器专门用于优化目的。同样,正因为直接执行机器代码比解释Java字节码至少快十倍。和Python一样,JVM下的JIT编译器对于Java语言设计人员和Java程序开发人员来说都是绝对透明的。JVM可以使用和不使用JIT编译器实现相同的Java编程语言。以同样的方式,相同的程序可以在有和没有JIT的jvm中执行,相同的程序将在两个jvm(有和没有JIT)上演示完全相同的行为,并从相同的输入产生相同的输出。和Python的情况一样,它们之间唯一可观察到的区别将是执行速度和JVM消耗的内存量。最后,像Python一样,Java中的JIT也不是语言设计中不可避免的一部分,而只是主要JVM实现的可选扩展。

从Java和Python的虚拟机的设计和实现的角度来看,它们有很大的不同,但(注意!)它们仍然是虚拟机。JVM是一种底层虚拟机,基本操作简单,指令调度成本高。Python是一种高级虚拟机,对于它,指令展示了复杂的行为,指令分派成本不是那么重要。Java以非常低的抽象级别运行。JVM操作于定义良好的一小组基本类型,并且字节码指令和本机机器码指令之间有非常紧密的对应关系(通常是一对一)。相反,Python虚拟机在高抽象级别上运行,它操作复杂的数据类型(对象),并支持特殊的多态性,而字节码指令公开复杂的行为,这些行为可以由一系列多个本机机器码指令表示。例如,Python支持无界范围数学。因此,Python VM被迫利用长算术来处理潜在的大整数,其操作结果可能会溢出机器字。因此,Python中的算术字节码指令可以公开到Python VM内部的函数调用中,而JVM中的算术操作将公开到由一个或几个本机机器指令表示的简单操作中。

因此,我们可以得出以下结论。Java虚拟机但Python解释器是因为:

  1. 虚拟机的术语假设二进制字节码解释,而解释器的术语假设程序文本解释。
  2. 历史上,Java是为二进制字节码解释而设计和实现的,而Python最初是为程序文本解释而设计和实现的。因此,术语“Java虚拟机”是历史悠久的,并且在Java社区中得到了很好的确立。类似地,术语“Python解释器”是历史悠久的,并且在Python社区中得到了很好的建立。人们倾向于延续传统,使用很久以前使用过的相同术语。
  3. 最后,目前对于Java来说,二进制字节码解释是程序执行的主要方式,而jit编译只是一种可选的透明优化。而对于Python来说,目前程序文本解释是Python程序执行的主要方式,而编译成Python VM字节码只是一种可选的透明优化。

因此,Java和Python的虚拟机都是二进制字节码解释器,这可能会导致诸如“为什么是Java虚拟机,而不是Python解释器?”之类的混淆。这里的关键点是,对于Python来说,虚拟机不是程序执行的主要或必要手段;它只是经典文本解释器的可选扩展。另一方面,虚拟机是Java程序执行生态系统中不可避免的核心部分。编程语言设计的静态或动态类型选择主要只影响虚拟机抽象级别,但并不决定是否需要虚拟机。使用这两种类型系统的语言可以被设计为在虚拟机环境中编译、解释或执行,这取决于它们所需的执行模型。

Python可以解释代码而不将其编译为字节码。Java不

Python是一种解释型语言,而不是编译型语言,尽管由于字节码编译器的存在,两者的区别可能很模糊。这意味着源文件可以直接运行,而无需显式地创建一个可执行文件,然后再运行。

(来自文档)。

在java中,每个必须文件都被编译为.class文件,然后在JVM上运行。相反,python会通过主脚本导入这些文件,以帮助加快后续使用这些文件的速度。

然而,在典型的情况下,大多数python(至少是CPython)代码运行在模拟的堆栈机器中,它与JVM的指令几乎相同,因此没有太大的区别。

然而,这种区别的真正原因是,从一开始,java就把自己打上了“可移植的、可执行的字节码”的标签,而python则把自己打上了带有REPL的动态解释语言的标签。名字贴!

我认为两者之间的界限是模糊的,人们大多争论的是“解释器”这个词的含义,以及语言与“解释器……编译器”范围的每一方有多接近。然而,没有一个是100%的。我认为编写Java或Python实现是很容易的,这是频谱的任何价值。

目前Java和Python都有虚拟机和字节码,尽管一个操作具体的值大小(如32位整数),而另一个必须确定每次调用的大小,在我看来,这并没有定义术语之间的边界。

Python没有正式定义的字节码,它只存在于内存中,这一论点也不能说服我,只是因为我计划开发只识别Python字节码的设备,编译部分将在浏览器JS机器中完成。

性能只与具体的实现有关。我们不需要知道对象的大小就能处理它,最后,在大多数情况下,我们处理的是结构,而不是基本类型。可以通过重用现有对象来优化Python VM,从而消除每次在表达式计算期间创建新对象的需要。一旦完成,在计算两个整数的和之间没有全局性能差异,这是Java的闪光点。

两者之间没有致命的区别,只有一些与最终用户无关的实现上的细微差别和缺乏优化,可能在她开始注意到性能滞后的时候,但这又是实现而不是架构的问题。

对于那些提到python不需要生成字节代码的帖子,我不确定这是真的。似乎Python中的所有可调用对象都必须有一个包含字节代码的.__code__.co_code属性。我不认为仅仅因为编译后的工件可能无法保存就称python为“未编译”有任何意义;并且在Python中通常不会被设计保存,例如所有的理解式编译输入的新字节码,这就是在compile(mode='exec, ...)和compile compile(mode='single', ...)之间理解变量范围不一致的原因,例如在运行Python脚本和使用pdb之间

HotSpot运行时被称为虚拟机,而CPython仅仅被称为解释器,这可能是有原因的

首先,CPython只是普通的、基于栈的字节码解释器。你向它输入Python操作码,CPython内部的软件堆栈机器就会计算你的代码,就像普通的解释器一样。

Java HotSpot运行时是不同的。首先也是最重要的是,Java有3个即时编译器,C1、C2和一个尚未使用的实验性编译器。但这不是主要原因。JVM中的解释器是一种非常特殊的解释器,称为模板解释器。与像CPython那样直接在大量操作码开关case语句中执行字节码不同(实际上几乎所有其他解释器都这样做),JVM中的模板解释器包含一个巨大的数组列表。它包含什么?字节码和本机CPU指令的键值对!数组列表在启动时是空的,充满了字节码的映射,指向本机机器语言,在应用程序启动之前直接在硬件上运行,这意味着"解释器"在JVM内部实际上不是一个解释器,它实际上是一个折扣编译器!当Java字节码运行时,“解释器”;简单地将输入字节码直接映射到本机机器语言,并直接执行本机映射,而不是在软件中实现它。我不太确定JVM为什么要这样做,但我怀疑它是为了更容易地执行“解释”。无缝地与JIT编译代码一起编写代码,并提高速度/性能。如果你将没有JIT的JVM与CPython或大多数其他解释器进行比较,它仍然可能领先于它们,因为它的巧妙设计,据我所知,之前没有其他语言使用过。