Java 解释器或任何解释器究竟是如何工作的



我一直在弄清楚口译员的确切工作,在谷歌上搜索并得出了一些结论,只是希望有人可以让我更好地了解口译员的工作。

所以我的理解是:

  1. 解释器是一种从高级转换代码的软件程序 语言到机器格式。
  2. 特别是关于Java解释器,它以二进制格式获取代码 (之前由Java编译器从源代码转换为字节码)。
  3. 现在Java解释器的平台是JVM,它在其中运行,所以 基本上,它将生成可以由JVM运行的代码。
  4. 所以它需要字节码产生中间代码和目标机器 代码并将其提供给 JVM。
  5. JVM
  6. 依次在JVM所在的操作系统平台上执行该代码 已实现或正在运行。

现在我仍然不清楚两者之间发生的子过程,即

  1. 解释器生成中间代码。
  2. 然后优化解释代码。
  3. 然后生成目标代码
  4. 最后被处决。

还有一些问题:

  • 那么解释器是否单独负责生成目标代码? 执行它 ?
  • 执行
  • 是否意味着它在 JVM 或底层操作系统中执行?

解释器是一种将代码从高级语言转换为机器格式的软件程序。

不。 这是一个编译器。 解释器是一种计算机程序,它直接执行用语言编写的指令。 这与将高级语言转换为较低语言的编译器不同。 C 编译器从 C 到汇编代码,汇编程序(另一种类型的编译器)从汇编代码转换为机器代码 - 现代 C 编译器执行从 C 到机器代码的两个步骤。

在 Java 中,Java 编译器执行代码验证并从 Java 源代码转换为字节码类文件。 它还执行许多小的处理任务,例如常量的预计算(如果可能),字符串的缓存等。

现在Java解释器的平台是JVM,它在其中运行,所以基本上它将产生可以由JVM运行的代码。

JVM直接在字节码上运行。 java解释器与JVM集成得如此紧密,以至于它们不应该被视为单独的实体。 同样正在发生的是大量的优化,其中字节码基本上是动态优化的。 这使得仅称其为口译员是不够的。 见下文。

所以它把字节码产生中间代码和目标机器码,交给JVM。

JVM正在执行这些转换。

JVM

依次在实现或运行JVM的操作系统平台上执行该代码。

我宁愿说JVM使用字节码,优化的用户代码,包含java和本机代码的java库,以及操作系统调用来执行java应用程序。

现在我仍然不清楚介于两者之间的子进程,即 1. 解释器生成中间代码。2. 然后优化解释代码。3. 然后生成目标代码 4.最后被处决。

Java 编译器生成字节码。 当 JVM 执行代码时,步骤 2-4 发生在 JVM 内部的运行时。 它与C(例如)非常不同,C具有由不同实用程序运行的这些单独步骤。 不要将其视为"子进程",而将其视为JVM内部的模块。

那么解释器是否单独负责生成目标代码并执行它?

有点。 根据定义,JVM的解释器读取字节码并直接执行它。 但是,在现代 JVM 中,解释器与即时编译器 (JIT) 协同工作,动态生成本机代码,以便 JVM 可以更有效地执行代码。

此外,还有后处理"编译"阶段,这些阶段在运行时分析生成的代码,以便可以通过内联常用代码块和其他机制来优化本机代码。 这就是为什么 JVM 负载在启动时峰值如此之高的原因。 它不仅在 jar 和类文件中加载,而且实际上是在动态执行cc -O3

执行

是否意味着它在 JVM 或底层操作系统中执行?

虽然我们谈论的是执行代码的 JVM,但这在技术上是不正确的。 一旦字节码被转换为本机代码,JVM和Java应用程序的执行就由CPU和硬件架构的其余部分完成。

操作系统是执行所有流程和资源管理的基础,因此程序可以有效地共享硬件并有效地执行。 操作系统还为应用程序提供 API,以便轻松访问磁盘、网络、内存以及其他硬件和资源。

1)解释器是一种将代码从高级语言转换为机器格式的软件程序。

不對。 解释器是一种运行以某种语言表示的程序的程序,该语言不是计算机的本机机器代码。

在这个过程中可能会有一个步骤,其中源语言被解析并翻译成中间语言,但这不是解释者的基本要求。 在 Java 案例中,字节码语言的设计使得既不需要解析,也不需要单独的中间语言。

2)具体说到Java解释器,它以二进制格式获取代码(早期由Java编译器从源代码转换为字节码)。

正确。 "二进制格式"是Java字节码。

3)现在Java解释器的平台是JVM,它在其中运行,所以基本上它将产生可以由JVM运行的代码。

不對。 字节码解释器是 JVM的一部分。 解释器不在 JVM 上运行。 字节码解释器不会产生任何东西。 它只运行字节码。

4)所以它把字节码产生中间码和目标机器码,交给JVM。

不對。

5)JVM

依次在实现或运行JVM的操作系统平台上执行该代码。

不對。

真实的故事是这样的:

  • JVM有许多组件。
  • 一个组件是字节码解释器。 它几乎直接执行字节码1。 您可以将解释器视为其指令集为字节码的抽象计算机的仿真器。
  • 第二个组件是 JIT 编译器。 这会将字节码转换为目标计算机的本机机器代码,以便目标硬件可以执行。

1 - 典型的字节码解释器会做一些工作,将抽象堆栈帧和对象布局映射到涉及目标特定大小和偏移量的具体帧和对象布局。 但将其称为"中间代码"是一种延伸。 解释器实际上只是增强了字节码。

给出一个 1000 英尺的视图,希望能澄清问题:

Java 应用程序有两个主要步骤:编译运行时。每个过程都有非常不同的功能和目的。两者的主要流程概述如下:

汇编

  • 这(通常)由通常在工具.jar文件中找到的[com.sun.tools.javac][1]执行,传统上在您的 $JAVA_HOME 中 - 与 java.jar 等相同的地方。
  • 这里的目标是将.java源文件转换为包含 java 运行时环境"配方"的.class文件。

编译步骤:

  1. 解析:读取文件,并去除其"边界"语法字符,例如大括号、分号和括号。这些存在是为了告诉解析器将每个源组件转换为哪个 java 对象(下一点将详细介绍)。
  2. AST 创建:抽象语法树是源文件的表示方式。这是一个字面上的"树"数据结构,其根类是[com.sun.tools.JCTree][3]。总体思路是每个表达式和每个语句都有一个java对象。在这个时间点,对每个代表的实际"类型"知之甚少。 在创建 AST 时检查的唯一内容是文字语法
  3. 脱糖:这是将 for 循环和其他语法糖翻译成更简单形式的地方。该语言仍然是"树"形式而不是字节码,因此这很容易发生
  4. 类型检查/推理:编译器变得复杂的地方。Java是一种静态语言,因此编译器必须使用访问者模式检查AST,并在tim之前找出所有内容的类型,并确保在运行时所有内容(好吧,几乎)对于类型,方法签名等都是合法的。如果某些内容太模糊或无效,编译将失败。
  5. 字节码:检查控制流以确保程序执行逻辑有效(没有无法访问的语句等)如果所有内容都通过检查而没有错误,则 AST 将转换为程序表示的字节码。
  6. .class文件写入:此时,将写入类文件。本质上,字节码是专用机器码之上的一小层抽象。这使得可以移植到其他机器/CPU 结构/平台,而不必担心它们之间相对较小的差异。

运行

  • 每个计算机平台都有不同的运行时环境/虚拟机实现。Java API 是通用的,但运行时环境是一个完全独立的软件。
  • JRE 只知道如何将类文件中的字节码转换为与目标平台兼容的机器码,并且针对相应平台也进行了高度优化。
  • 有许多不同的运行时/虚拟机实现,但最受欢迎的是热点虚拟机。
  • VM 非常复杂,可在运行时优化代码。启动时间很慢,但它基本上会"学习"。
  • 这是"JIT"(即时)概念的实际应用 - 编译器通过检查正确的类型和语法来完成所有繁重的工作,而 VM 只是将字节码转换并优化为机器代码。

也。。。

  • Java 编译器 API 在 JSR 199 下进行了标准化。虽然不完全属于同一事物(找不到确切的JLS),但许多其他语言和工具利用标准化的编译过程/API来使用Oracle提供的高级JVM(运行时)技术,同时允许不同的语法。
    • 参见Scala,Groovy,Kotlin,Jython,JRuby等。所有这些都利用了 Java 运行时环境,因为它们转换了不同的语法以与 Java 编译器 API 兼容!它非常整洁 - 任何人都可以使用他们想要的任何语法编写高性能语言,因为两者解耦。JVM的几乎所有语言都有改编

我会根据我创建DSL的经验来回答。

C 被编译是因为您运行将源代码传递给 gcc 并以机器代码运行存储的程序。

Python 之所以被解释,是因为您可以通过将程序源传递给解释器来运行程序。解释器读取源文件并执行它。

Java是两者的混合体,因为您将Java文件"编译"为字节码,然后调用JVM来运行它。字节码不是机器码,它需要由JVM解释。Java介于C和Python之间,因为你不能做像"eval"这样的花哨的事情(在运行时评估代码或表达式块,就像在Python中一样)。但是,Java具有C程序不可能具备的反射能力。简而言之,Java运行时的设计介于纯编译语言和解释语言之间的中间层,在性能和灵活性方面提供了两个词中最好的(和最差的)。

但是,Python也有一个虚拟机,它有自己的字节码格式。这同样适用于Perl,Lua等。这些解释器首先将源文件转换为字节码,然后解释字节码。

我一直想知道为什么这样做是必要的,直到我为模拟DSL制作了自己的解释器。我的解释器进行词法分析(在标记中分解源),将其转换为抽象语法树,然后通过遍历来评估树。出于软件工程的缘故,我使用了一些设计模式,我的代码大量使用多态性。与处理模仿真实计算机体系结构的高效字节码格式相比,这是非常慢的。如果我创建自己的虚拟机或使用现有的虚拟机,我的模拟会更快。例如,对于计算长数值表达式,将其转换为类似于汇编代码的内容比处理抽象树的分支更快,因为它需要调用大量多态方法。

有两种执行程序的方法。

  • 通过编译器:这将编程语言(例如.c)中的文本解析为机器代码,在Windows.exe上。然后可以独立于编译器执行此操作。

此编译可以通过将多个.c文件编译为多个目标文件(中间产品),然后将它们链接到单个应用程序或库中来完成。

  • 通过解释器:这解析编程语言中的文本(例如.java)并"立即"执行程序。

对于Java,这种方法有点混合/堆叠:Java编译器javac编译.java.class文件,并可能将它们压缩为.jar(或.war.ear...)。.class文件由一个更抽象的字节码组成,用于抽象堆栈机。

然后 java 运行时java(调用 JVM、java 虚拟机或字节码解释器)可以执行 .class/.jar。这实际上是java字节码的解释器。

如今,它还在运行时将字节码的(部分)转换为机器码。这也称为字节码到机器码的即时编译器。


总之: -compiler只是创建代码; - 立即执行interpreter

解释器将遍历解析的命令/高级中间代码,并用一段代码解释每个命令。间接和原则上缓慢。

最新更新