Python的“虚拟机”似乎很少读到,而在Java中“虚拟机”一直被使用。
两者都解释字节码;为什么一个叫虚拟机,另一个叫解释器?
他们之间没有真正的区别,人们只是遵循创造者选择的惯例。
术语不同的一个原因可能是,人们通常认为向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接口,但试图交互式地编写任何静态类型的代码 会很乏味,所以不是那样做的。
可移植的解释静态类型语言
仅次于LLVM的是LLVM,但LLVM在不同的级别上运行:
可移植解释汇编语言
在虚拟机中发生的每个操作最终都必须在实际硬件上进行。虚拟机包含预编译的例程,这些例程足够通用,可以执行任何逻辑操作的组合。虚拟机可能不会发出新的机器指令,但它肯定会以任意复杂的序列反复执行自己的例程。Java虚拟机、Python虚拟机和所有其他通用虚拟机在某种意义上是相同的,因为它们可以被诱导执行您能想到的任何逻辑,但是它们在执行哪些任务以及它们留给程序员的任务方面是不同的。
permalink
首先,你应该明白,编程或计算机科学一般不是数学,我们经常使用的大多数术语都没有严格的定义。
现在回答你的问题:
什么是解释器(计算机科学)
它按最小的可执行单元翻译源代码,然后执行该单元。
什么是虚拟机
对于JVM来说,虚拟机是一个包含解释器、类加载器、垃圾收集器、线程调度器、JIT编译器和许多其他东西的软件。
正如你所看到的,解释器是JVM的一部分,整个JVM不能被称为解释器,因为它包含许多其他组件。
为什么在谈论python时要用“解释器”这个词
不,它们并不都解释字节码。
Python只在运行pypy时解释字节码。否则,它将被编译成C并在该级别上进行解释。
Java编译为字节码。
为了对“为什么是Java虚拟机,而不是Python解释器?”问题提供一个深入的答案,让我们尝试回到编译理论领域,作为讨论的出发点。
程序编译的典型过程包括以下步骤:
a = b + c
a
好的。现在我们来定义这些术语。
翻译,在这个词的经典含义中,基于直接从程序文本产生的基于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,而不需要任何形式的编译或转换为任何形式的二进制代码)。
可以用类似的方式来考虑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解释器是因为:
因此,Java和Python的虚拟机都是二进制字节码解释器,这可能会导致诸如“为什么是Java虚拟机,而不是Python解释器?”之类的混淆。这里的关键点是,对于Python来说,虚拟机不是程序执行的主要或必要手段;它只是经典文本解释器的可选扩展。另一方面,虚拟机是Java程序执行生态系统中不可避免的核心部分。编程语言设计的静态或动态类型选择主要只影响虚拟机抽象级别,但并不决定是否需要虚拟机。使用这两种类型系统的语言可以被设计为在虚拟机环境中编译、解释或执行,这取决于它们所需的执行模型。
Python可以解释代码而不将其编译为字节码。Java不。
Python是一种解释型语言,而不是编译型语言,尽管由于字节码编译器的存在,两者的区别可能很模糊。这意味着源文件可以直接运行,而无需显式地创建一个可执行文件,然后再运行。
(来自文档)。
在java中,每个必须文件都被编译为.class文件,然后在JVM上运行。相反,python会通过主脚本导入这些文件,以帮助加快后续使用这些文件的速度。
.class
然而,在典型的情况下,大多数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之间
.__code__.co_code
compile(mode='exec, ...)
compile(mode='single', ...)
HotSpot运行时被称为虚拟机,而CPython仅仅被称为解释器,这可能是有原因的
首先,CPython只是普通的、基于栈的字节码解释器。你向它输入Python操作码,CPython内部的软件堆栈机器就会计算你的代码,就像普通的解释器一样。
Java HotSpot运行时是不同的。首先也是最重要的是,Java有3个即时编译器,C1、C2和一个尚未使用的实验性编译器。但这不是主要原因。JVM中的解释器是一种非常特殊的解释器,称为模板解释器。与像CPython那样直接在大量操作码开关case语句中执行字节码不同(实际上几乎所有其他解释器都这样做),JVM中的模板解释器包含一个巨大的数组列表。它包含什么?字节码和本机CPU指令的键值对!数组列表在启动时是空的,充满了字节码的映射,指向本机机器语言,在应用程序启动之前直接在硬件上运行,这意味着"解释器"在JVM内部实际上不是一个解释器,它实际上是一个折扣编译器!当Java字节码运行时,“解释器”;简单地将输入字节码直接映射到本机机器语言,并直接执行本机映射,而不是在软件中实现它。我不太确定JVM为什么要这样做,但我怀疑它是为了更容易地执行“解释”。无缝地与JIT编译代码一起编写代码,并提高速度/性能。如果你将没有JIT的JVM与CPython或大多数其他解释器进行比较,它仍然可能领先于它们,因为它的巧妙设计,据我所知,之前没有其他语言使用过。