问题标签 [vm-implementation]

For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.

0 投票
10 回答
4727 浏览

compiler-construction - 为什么需要虚拟机?

我正在阅读这个问题以找出 Java 虚拟机和 .NET CLR 之间的区别,而 Benji 的回答让我想知道为什么首先需要虚拟机。

根据我对 Benji 解释的理解,虚拟机的 JIT 编译器将中间代码解释为在 CPU 上运行的实际汇编代码。之所以必须这样做,是因为 CPU 通常具有不同数量的寄存器,并且根据 Benji 的说法,“有些寄存器是专用的,每条指令都希望其操作数位于不同的寄存器中。” 这是有道理的,因此需要像虚拟机这样的中间解释器,以便可以在任何 CPU 上运行相同的代码。

但是,如果是这样的话,那么我不明白为什么编译成机器代码的 C 或 C++ 代码能够在任何计算机上运行,​​只要它是正确的操作系统。那么为什么我在使用 Pentium 的 Windows 机器上编译的 C 程序能够在使用 AMD 的其他 Windows 机器上运行?

如果 C 代码可以在任何 CPU 上运行,那么虚拟机的目的是什么?是否可以在任何操作系统上运行相同的代码?我知道 Java 在几乎任何操作系统上都有 VM 版本,但是除了 Windows 之外,还有其他操作系统的 CLR 吗?

还是我还缺少其他东西?操作系统是否对其运行的汇编代码进行其他解释以使其适应特定的 CPU 或其他东西?

我很好奇这一切是如何运作的,因此将不胜感激。

注意:我不只是在 JVM 与 CLR 问题中将我的查询作为评论发布的原因是因为我还没有足够的积分来发表评论 =b。

编辑:感谢所有伟大的答案!所以似乎我缺少的是,尽管所有处理器都有差异,但有一个共同的标准化,主要是 X86 架构,它提供了足够多的通用特性集,以便在一个 X86 处理器上编译的 C 代码大部分都可以工作在另一个 X86 处理器上。这进一步证明了虚拟机的合理性,更不用说我忘记了垃圾收集的重要性。

0 投票
6 回答
668 浏览

java - Squeak 跨平台运行“位相同”是什么意思,而 Java 却没有?

Alan Kay指出“与 Java 不同,[Squeak] 在每台机器上的运行方式都完全相同——这是我们 20 年前发明的”。维基百科页面也提到了这一点:

Squeak 可用于许多平台,并且在一个平台上生成的程序在所有其他平台上运行位相同。

由于具有不同指令集的机器显然不能在本地运行位相同的程序,当有人说 Squeak 在不同的机器上运行位相同的程序时,这是什么意思,而 Java 没有?

我的印象是编译后的 Java 类在任何 JVM 上的任何机器上运行相同,不是这样吗?

0 投票
2 回答
4185 浏览

python - Python Virtual Machine architecture diagrams/references

Someone could point out sites/books where I can find introductory documentation about the architecture of the Python VM?

I'm interested in the C version, but if there are easy-to-follow references about other implementations it could be helpful too.

I'm trying to find any kind of resources of higher level than plain source code (however, UML diagrams or something like that would be nice) And also, information about the design decisions involved, including tradeoffs between the different factors (performance, stability, simplicity).

0 投票
1 回答
6391 浏览

vm-implementation - 基于寄存器的虚拟机如何工作?

基于寄存器的虚拟机如何工作?我正在寻找有关基于寄存器的虚拟机如何工作的介绍。有人可以帮忙吗?

谢谢你。

0 投票
3 回答
1590 浏览

compiler-construction - 除了实际破解代码之外,了解 VM 实现的最佳方法是什么?

我想了解有关 VM 实现和优化的更多信息。现在我正在为 JRuby 做出贡献(以一种小的方式),并且正在使用我自己的在 VM 中运行的类似 lisp 的语言实现来玩/编写。

但是,我想获得有关使用 VM 和设计它们的更多信息。除了阅读/使用现有代码之外,是否有此类信息的良好资源?我不反对这样做,我只是想知道是否还有其他我可以调查的来源。

0 投票
9 回答
974 浏览

c++ - 为什么很多虚拟机看起来都具有 C++ 特性,却是用 C 编写的?

我注意到一些不太古老的 VM 语言,例如用 C 编写的 Lua、NekoVM 和 Potion。

看起来他们正在重新实现许多 C++ 特性。

用 C 而不是 C++ 编写它们有什么好处吗?

0 投票
2 回答
736 浏览

c - 无堆栈 VM 实现会出现哪些 C 集成问题?

我所说的无堆栈VM是指在堆上维护自己的堆栈而不是使用系统“C-stack”的实现。这有很多优点,如延续和可序列化状态,但在涉及 C 绑定时也有一些缺点,尤其是 C-VM-C 类型的回调(或 VM-C-VM)。

问题是这些缺点到底是什么?任何人都可以举一个真正问题的好例子吗?

0 投票
1 回答
383 浏览

ruby - RubyVM 中的 putspecialobject 操作码是如何工作的?

我正在研究 RubyVM 的实现,并且我到处搜索有关操作码的一些文档,但无济于事。

如果有人具体知道putspecialobject操作码是如何工作的,或者只是一个指向一些完整文档的链接,我将不胜感激!

0 投票
5 回答
5535 浏览

stack - 虚拟机是如何工作的?

我一直在研究编程语言是如何工作的,其中一些具有所谓的虚拟机。我知道这是在另一种编程语言中对编程语言进行某种形式的仿真,并且它的工作方式类似于使用堆栈执行编译语言的方式。我做对了吗?

有了我所做的附带条件,让我感到困惑的是,许多非编译语言允许具有“自由”类型系统的变量。例如,在 Python 中,我可以这样写:

字符串和大整数是完全不相关的,并且在内存中占用的空间量不同,那么这段代码怎么能在基于堆栈的环境中表示呢?这里到底发生了什么?x 是否指向堆栈上的新位置并且未引用旧的字符串数据?这些语言不使用堆栈吗?如果不是,它们如何在内部表示变量?

0 投票
4 回答
3422 浏览

performance - VM 设计:更多操作码还是更少操作码?什么是更好的?

不要感到震惊。这是很多文字,但恐怕如果不提供一些详细信息,我将无法真正展示这一切的全部内容(并且可能会得到很多并不能真正解决我的问题的答案)。这绝对不是一项任务(正如某人在他的评论中荒谬地声称的那样)。

先决条件

由于这个问题可能根本无法回答,除非至少设置了一些先决条件,所以这里是先决条件:

  • 应解释虚拟机代码。不禁止可能存在 JIT 编译器,但设计应针对解释器。
  • VM 应该是基于寄存器的,而不是基于堆栈的。
  • 答案可能既不假设有一组固定的寄存器,也不假设它们的数量是无限的,两者都可能是这种情况。

此外,我们需要更好地定义“更好”。有几个属性必须考虑:

  1. VM 代码在磁盘上的存储空间。当然,您总是可以在这里放弃所有优化并仅压缩代码,但这对(2)有负面影响。
  2. 解码速度。如果将代码转换为可以直接执行的东西需要太长时间,那么存储代码的最佳方式是无用的。
  3. 内存中的存储空间。此代码必须在有或没有进一步解码的情况下直接可执行,但如果涉及进一步解码,则在执行期间和每次执行指令时完成此编码(在加载代码时仅完成一次解码计入第 2 项)。
  4. 代码的执行速度(考虑到常见的解释器技术)。
  5. VM 的复杂性以及为其编写解释器的难度。
  6. VM 自身需要的资源量。(如果 VM 运行的代码大小为 2 KB 并且执行速度比眨眼快,这不是一个好的设计,但是它需要 150 MB 来执行此操作,并且它的启动时间远高于代码的运行时间它执行)

现在举例说明我实际上所说的或多或少的操作码。看起来实际上设置了操作码的数量,因为每次操作需要一个操作码。然而它并不那么容易。

同一操作的多个操作码

您可以进行类似的操作

将 R1 和 R2 的值相加,将结果写入 R3。现在考虑以下特殊情况:

这些是您可以在许多应用程序中找到的常见操作。您可以使用已经存在的操作码来表达它们(除非您需要不同的操作码,因为最后一个操作码具有 int 值而不是寄存器)。但是,您也可以为这些创建特殊的操作码:

和之前一样。优势在哪里?ADD2 只需要两个参数,而不是 3,INC 甚至只需要一个。因此,这可以在磁盘和/或内存中进行更紧凑的编码。由于将任何一种形式转换为另一种形式也很容易,因此解码步骤可以在两种方式之间转换以表达这些陈述。不过,我不确定这两种形式会在多大程度上影响执行速度。

将两个操作码组合成一个

现在让我们假设您有一个 ADD_RRR(R 代表寄存器)和一个 LOAD 来将数据加载到寄存器中。

您可以拥有这两个操作码并始终在整个代码中使用这样的结构......或者您可以将它们组合成一个新的操作码,名为 ADD_RMR(M 代表内存)

数据类型与操作码

假设您有 16 位整数和 32 位整数作为本机类型。寄存器是 32 位的,因此任何一种数据类型都适合。现在,当您添加两个寄存器时,您可以将数据类型设为参数:

例如,有符号和无符号整数也是如此。这样 ADD 可以是一个短操作码,一个字节,然后你有另一个字节(或者可能只是 4 位)告诉 VM 如何解释寄存器(它们是 16 位还是 32 位值)。或者您可以废弃类型编码,而使用两个操作码:

有人可能会说两者完全相同 - 只需将第一种方式解释为 16 位操作码即可。是的,但是一个非常天真的解释器可能看起来完全不同。例如,如果每个操作码有一个函数并使用 switch 语句进行调度(不是最好的方法,函数调用开销,switch 语句也可能不是最优的,我知道),两个操作码可能如下所示:

每个功能都以某种添加为中心。第二个可能看起来像这样:

将子交换机添加到主交换机或将子调度表添加到主调度表。当然,无论类型是否显式,解释器都可以做任何一种方式,但根据操作码设计,任何一种方式都会让开发人员感觉更原生。

元操作码

由于没有更好的名字,我会这样称呼他们。这些操作码本身没有任何意义,它们只是改变了后面的操作码的含义。就像著名的 WIDE 运算符:

例如,在第二种情况下,寄存器是 16 位的(因此您可以添加更多),在第一种情况下只有 8 个。或者,您不能有这样的元操作码,并且有一个 ADD 和一个 ADD_WIDE 操作码。像 WIDE 这样的元操作码避免使用 SUB_WIDE、MUL_WIDE 等,因为您始终可以在所有其他正常操作码之前添加 WIDE(始终只有一个操作码)。缺点是单独的操作码变得毫无意义,您必须始终检查它之前的操作码是否是元操作码。此外,VM 必须为每个线程存储一个额外的状态(例如,我们现在是否处于宽模式)并在下一条指令之后再次删除该状态。甚至 CPU 也有这样的操作码(例如 x86 LOCK 操作码)。

如何找到一个好的权衡???

当然,您拥有的操作码越多,开关/调度表就会变得越大,在磁盘或内存中表达这些代码所需的位数就越多(尽管您可以更有效地将它们存储在数据不存在的磁盘上必须由 VM 直接执行);此外,VM 将变得更加复杂,代码行数也更多——另一方面,操作码越强大:您越来越接近每个表达式,即使是复杂的表达式,都将在一个操作码中结束的地步。

选择小的操作码可以很容易地对 VM 进行编码,并且我猜会导致非常紧凑的操作码 - 另一方面,这意味着您可能需要大量的操作码来执行简单的任务,并且每个不经常使用的表达式都必须成为某种(本机)函数调用,因为它不能使用任何操作码。

我在 Internet 上阅读了很多关于各种 VM 的信息,但没有任何消息来源能够真正做出良好且公平的权衡。设计 VM 就像设计 CPU,有些 CPU 的操作码很少,它们速度很快,但您也需要很多这样的 CPU。并且有许多操作码的 CPU,有些非常慢,但你需要更少的操作码来表达相同的代码。看起来“越多越好”的CPU完全赢得了消费市场,而“越少越好”的CPU只能在服务器市场或超级计算机业务的某些部分生存。虚拟机呢?