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

时间:2009-01-14 03:39:33

标签: java python jvm

在Java“虚拟机”中一直使用时,读取Python“虚拟机”似乎很少见。

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

14 个答案:

答案 0 :(得分:146)

答案 1 :(得分:123)

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

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

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

VM和解释器之间差异的另一个测试是你是否认为它与语言无关。我们所知道的Java VM并不是特定于Java的。您可以使用其他语言生成编译器,从而生成可在JVM上运行的字节代码。另一方面,我认为我们不会真的想到将Python以外的其他语言“编译”到Python中以供Python解释器解释。

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

答案 2 :(得分:57)

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

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

即使Python使用虚拟机,从用户的角度来看,大多数时候都可以忽略这个细节。

答案 3 :(得分:14)

解释器,将源代码转换为一些有效的中间表示(代码)并立即执行此操作。

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

虚拟机的一个非常重要的特性是内部运行的软件仅限于虚拟机提供的资源。确切地说,它无法打破虚拟世界。考虑远程代码Java Applet的安全执行。

在python的情况下,如果我们保留 pyc 文件,如本文评论中所述,那么该机制将变得更像VM,并且这个字节码执行得更快 - 它会仍然可以解释,但从计算机更友好的形式。如果我们整体看一下,PVM是Python Interpreter的最后一步。

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

更多信息,维基百科条目:InterpreterVirtual Machine。还有一个here。在这里你可以找到Comparison of application virtual machines。它有助于理解编译器,解释器和VM之间的区别。

答案 4 :(得分:12)

术语解释器是一个可以追溯到早期shell脚本语言的遗留术语。随着“脚本语言”逐渐演变为全功能语言,并且它们的相应平台变得更加复杂和沙盒化,虚拟机和解释器(在Python意义上)之间的区别非常小或不存在。

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

答案 5 :(得分:9)

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

答案 6 :(得分:5)

要对“ 为什么要使用Java虚拟机,但要使用Python解释器?”这一问题提供深入的答案,让我们尝试回到编译理论领域来作为讨论的起点。 / p>

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

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

好的。现在让我们定义术语。

解释器,在该词的经典含义中,假定基于直接从程序文本中产生的基于AST的程序评估执行。在这种情况下,程序将以源代码的形式分发,并且解释器通常是通过动态方式(逐条语句或逐行)由程序文本提供的。对于每个输入语句,解释器将构建其AST并立即对其进行评估以更改程序的“状态”。这是脚本语言演示的典型行为。考虑一下Bash,Windows CMD等。从概念上讲,Python也采用这种方式。

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

最后,如果将机器代码生成添加到字节码编译器中,则可以实现所谓的经典编译器。传统的编译器将程序源代码转换为特定处理器的机器代码。然后可以在目标处理器上直接直接执行该机器代码,而无需任何其他中介(没有任何类型的解释器,既没有文本解释器也没有二进制解释器)。

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

Java 最初被设计为具有尽可能少的实现依赖性。它的设计基于“一次编写,随处运行”(WORA)的原则。为了实现它, Java 最初被设计为一种编程语言,可以编译为独立于机器的二进制字节码,然后可以在所有支持 Java < / em>,而无需重新编译。您可以考虑 Java ,就像基于WORA的 C ++ 一样。实际上, Java Python 之类的脚本语言更接近 C ++ 。但是与 C ++ 相比, Java 被设计为编译为二进制字节码,然后在虚拟机环境中执行,而 C ++ 被设计为用机器代码编译,然后由目标处理器直接执行。

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

因此,从历史上看, Java 开发人员倾向于谈论 Java虚拟机(因为最初, Java 是< em> Java 字节码编译器和字节码解释器- JVM ),而 Python 开发人员则倾向于谈论 Python < / em>解释器(因为最初 Python 没有任何虚拟机,并且是一种经典的文本解释器,可以直接执行程序 text 而不进行任何形式的操作编译或转换为任何形式的二进制代码)。

当前,Python还具有虚拟机,可以编译和解释Python字节码。这个事实使“ 为什么要使用Java虚拟机,但要使用Python解释器?”这个混淆项又增加了一笔投资,因为这两种语言的实现似乎都包含虚拟机。 但!即使在目前,对程序文本的解释也是Python程序执行的主要方式。 Python实现将引擎盖下的虚拟机专门用作优化技术。与直接解释原始程序文本相比,在虚拟机中解释二进制字节码要有效得多。同时,对于Python语言设计人员和Python程序开发人员而言,Python中虚拟机的存在绝对透明。可以在带有和不带有虚拟机的解释器中实现相同的语言。以相同的方式,可以在带有和不带有虚拟机的解释器中执行相同的程序,并且这些程序将演示完全相同的行为,并从相等的输入产生相同的输出。唯一可观察到的差异是程序执行的速度和解释器消耗的内存量。因此,Python中的虚拟机并不是语言设计中不可避免的部分,而只是主要Python解释器的可选扩展。

可以类似的方式考虑Java。底层的Java具有JIT编译器,可以有选择地将Java类的方法编译为目标平台的机器代码,然后直接执行它。但! Java仍然使用字节码解释作为Java程序执行的主要方式。就像Python实施将引擎盖下的虚拟机专门用作优化技术一样,Java虚拟机仅将即时编译器用于优化目的。同样,仅因为直接执行机器代码比解释Java字节码至少快十倍的事实。就像Python一样,对于Java语言设计人员和Java程序开发人员而言,JVM罩下的JIT编译器的存在绝对是透明的。带有和不带有JIT编译器的JVM都可以实现相同的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程序执行生态系统中不可或缺的核心部分。编程语言设计的静态或动态类型选择主要只影响虚拟机抽象级别,但并不能决定是否需要虚拟机。可以将使用两种类型系统的语言设计为在虚拟机环境中进行编译,解释或执行,具体取决于它们所需的执行模型。

答案 7 :(得分:3)

不要忘记Python有可用于x86的JIT编译器,这进一步混淆了这个问题。 (见psyco)。

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

答案 8 :(得分:1)

Python可以解释代码,而无需将其编译为字节码。 Java无法

  

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

(来自文档)。

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

但是,在典型情况下,大多数python(至少是CPython)代码在仿真的堆栈计算机中运行,其指令与JVM的指令几乎相同,因此没有太大的区别。

然而,造成这种区分的真正原因是因为,从一开始,java就将其自身标识为“便携式可执行字节码”,而python则将其自身标识为具有REPL的动态解释语言。名字棒!

答案 9 :(得分:0)

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

现在问你的问题:

什么是口译员(在计算机科学中)

它通过最小的可执行单元转换源代码,然后执行该单元。

什么是虚拟机

在JVM的情况下,虚拟机是一个包含解释器,类加载器,垃圾收集器,线程调度程序,JIT编译器和许多其他东西的软件。

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

为什么要使用word&#34; Interpreter&#34;在谈论python时

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

答案 10 :(得分:0)

不,他们都不会解释字节码。

如果您使用pypy运行,Python只会解释字节码。否则,它将被编译为C并在该级别进行解释。

Java编译为字节码。

答案 11 :(得分:0)

我认为两者之间的界线是模糊的,人们大多围绕“解释器”一词的含义争论不休,以及该语言与“解释器...编译器”谱的各方面之间的距离如何。没有人能100%。我认为编写具有任何价值的Java或Python实现很容易。

目前,Java和Python都具有虚拟机和字节码,尽管其中一个按具体的值大小(例如32位整数)进行操作,而另一个则必须确定每次调用的大小,我认为这并未定义两者之间的边界。条款。

关于Python没有正式定义字节码并且仅存在于内存中的说法也并没有说服我,因为我正计划开发仅识别Python字节码且编译部分将在浏览器中完成的设备JS机器。

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

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

答案 12 :(得分:0)

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

答案 13 :(得分:0)

实际上可能有一个原因,为什么 HotSpot 运行时被称为虚拟机而 CPython 仅被称为解释器

首先,CPython 只是普通的、基于堆栈的字节码解释器。您将 Python 操作码输入其中,CPython 内部的软件堆栈机器会评估您的代码,就像普通的解释器一样。

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