【问题标题】:Are Java programs just instances of the JRE?Java 程序只是 JRE 的实例吗?
【发布时间】:2021-02-10 09:28:50
【问题描述】:

当您在 Windows 中运行 .exe 控制台应用程序(例如用 C++ 编写的应用程序)时,Windows 会为您创建一个控制台窗口。

因此,从本质上讲,该程序不会在 Windows 本身之外的任何东西之上运行。

当你调用java Main.classinside cmd.exe 控制台时,它真的是它自己的独立程序吗?感觉更像java 是正在运行的程序,而Main.class 只是一个给定的参数。

这一切都是要问,所有Java程序都是简单的控制台java [argument]程序吗?另一种问法是,所有 Java 程序都只是读取特定类文件的 JRE 程序/实例吗?

【问题讨论】:

  • 这里的部分问题是您的术语。 Java 程序是一组 .java 源文件或其编译结果:一组 .class 文件。 Java 进程确实是一个JVM进程。
  • @Nayuki 是的,这是真的。一些 Java 程序会这样做。这样它看起来更像是一个本地程序,例如当有人在任务管理器中查找该程序时。你可以给exe一个图标。或者,如有必要,请下载 exe 并安装 JVM。
  • 代码就是数据,数据就是代码。
  • @hobbs:除了在现代操作系统下,代码在页表中设置了执行和只读位。数据通常是可写的,但肯定是不可执行的。
  • 在某种程度上取决于上下文。在 IBM iSeries 上,Java 程序(可选地)被编译成“本机代码”并像任何其他程序一样执行。在这种情况下,JVM 只是程序的“运行时库”。即使没有这种编译,Java 程序也会被操作系统“识别”,并且不需要显式调用 Java.exe。

标签: java


【解决方案1】:

Java 程序被编译成一种称为Java bytecode 的中间语言。这些可以说是由 Java 运行时(实际上是 Java 虚拟机)解释的,但我相信它比这要复杂一些。

我确信某些代码是在运行时即时 (JIT) 编译的,这意味着 JRE 实际上会将一些字节码编译为实际的机器代码。它何时执行此操作以及出于何种原因的详细信息超出了我的知识范围,但通常出于性能原因执行此操作。我看到另一个答案为您提供了 JIT 信息的链接。

正如您在该 Wikipedia 链接中所注意到的,某些编译器(例如 GNU Java 编译器)可以直接编译为机器码。

你还会注意到它说一些特殊的处理器可以本地运行字节码,在这种情况下不需要 JVM。

哦,另一个注意事项:当程序运行时(在 JVM 中),它确实是“JVM 的实例”。如果您检查进程列表,您会发现您的程序是应用程序java 的一个实例。因此,如果您查看 Windows 上的任务管理器或 Mac 上的活动监视器或 Linux 上的进程 ID 列表,您将看到为您启动的每个 Java 程序运行一个 java 进程。

【讨论】:

  • 这个答案强调了纯解释和 JIT 编译之间的区别,但问题是关于 JVM 如何映射到操作系统中的进程。
  • Re "Process Monitor":你的意思是Task Manager
  • @PeterMortensen 是的,谢谢我的意思。
【解决方案2】:

是的,在某种程度上,每个 Java 程序都必须由 JDK(Java 开发工具包)编译并由 Java 开发工具 JRE(Java 运行时环境)运行。

当 Java 编译时,它会生成一个 .jre 或 .class,它们不能以任何方式直接运行到计算机处理器(有一些方法可以将 .jar 更改为 .exe),但它必须通过 JIT(即时)编译器运行 JVM(Java 虚拟机)。

有了这个图表,那么在某种程度上,是的,Java 程序类“属于”JRE。但它肯定比这更复杂。

我建议您阅读有关 JIT 的更多信息 here

【讨论】:

  • 不是 JRE java 运行环境吗?
  • Offt,谢谢你的更正我真的没有注意到
  • "当一个 java 编译它产生一个 .jre"。这并不正确。 Java 编译.java 文件以生成.class 文件。类文件以及其他资源文件通常被收集到.jar 文件中,JRE 可以读取其中任何一个文件。 JRE 解释 .class 文件的内容。
【解决方案3】:

为了更简单地说明这一点,答案是:是的(尽管您实际上是指 JVM 而不是 JRE)。操作系统运行的程序是 JVM(Java 虚拟机),Java 应用程序是该程序正在读取的数据。 JVM 就像 Microsoft Word,Java 程序就像 Word 文档。

这个问题涉及编译语言和解释语言之间的本质区别,正如here 所描述的那样。

进一步使用类比来解释 JVM 和 JRE 是什么......JVM 就像 Microsoft Word 程序本身,而 JRE 就像 MS Word 程序加上所有其他东西,如模板、示例文档、与它一起安装以支持其功能的字体等。

【讨论】:

  • Java 有一个编译器并生成字节码。我不确定这是否有帮助。这种情况与在虚拟机或容器中运行的 C 程序没有根本区别。你会说VM就像Word,而在VM中运行的C程序就像Word文档吗?
  • 如果编译语言和解释语言之间的界限比您想要的更模糊,绝对不要感到惊讶。这条线过去很清楚,但最近我们把事情越来越靠近混乱的中间。
  • @CortAmmon - 是的,你是对的。在更详细的层面上,“编译”和“解释”两个词的应用有时确实是一个模糊的东西。事实上,Java 两者兼而有之,对吧?它被编译为字节码,但随后该字节码在运行时由另一个程序解释。我认为在最基本的层面上使用这些术语可能是有价值的......如果操作系统直接运行你的程序,它就是“编译”的。如果它运行一些其他程序,然后将您的“程序”作为数据读取,则它是“解释的”。当然,这过于简单化了。
  • @Bruno 当我阅读上面的链接时,一切都很好。您只需要在编译时让它们可用。您多久将一个全新的类加载到现有应用程序中而不重建它?也许你有一个特殊的场景,但对我来说这似乎是一个不必要的复杂化。可能会有一个很好的用例,但我怀疑它是否普遍。
  • 你可以走得更远一点。现在大多数 CPU 指令都是作为微码实现的,所以即使你用汇编编写,你实际上只是在编写将由解释器解释的字节码(恰好存储在 CPU 中!)CPU 甚至可以更新微码解释器,就像最近为减轻 Spectre/Meltdown 错误所做的那样。
【解决方案4】:

这都是要问,所有Java程序都是简单的控制台java [argument]程序吗?

不是特别是,不,因为并非所有 Java 程序都通过 java 工具运行,但请继续阅读。

换一种方式问,是否所有 Java 程序都只是读取特定类文件的 JRE 程序/实例?

Java 程序通常由Java virtual machine (JVM) 运行,例如 Java 运行时环境中的那个,是的。也就是说,在大多数情况下,Java 程序(构成程序的类字节码和其他资源的集合,有时在 .jar/.war/.ear/etc. 文件中)由一个JVM 实例,由 java 工具或 servlet 容器(或者,过去是小程序容器)或其他知道如何生成 JVM 实例的环境启动。

通常是这样的:

  1. .java 文件编译为Java bytecode,通常输出为.class 文件。 Java 字节码是一种高级机器语言,不依赖于特定的 CPU 架构或操作系统。

  2. 有时,.class 文件(和其他资源)被捆绑到容器中(.jar 文件、.war 文件、.ear 文件等)。

  3. 运行程序时,您可以使用java 工具或servlet 容器或其他知道如何运行Java 字节码的进程。这些是特定于 CPU 和操作系统的,并包含或加载 JVM。

  4. 工具中的代码(java 或 servlet 容器或其他)加载字节码(来自.class 文件或类似文件)并将其传递给 JVM 以进行实例化和执行。根据 JVM,这可能只涉及解释字节码,或将其编译为特定于 CPU 和 OS 的机器代码(“即时”[JIT] 编译)并执行它,或两者的组合。例如,Sun 的 HotSpot JVM 至少会进行两级 JIT 编译,具体取决于是否使用了特定的代码段来编译它,如果是这样,是否足以证明积极优化它的合理性。

有些编译器将 Java 源代码或 Java 字节码编译为特定 CPU 架构和操作系统的机器码,输出一体化的可执行文件,但以上是常见的场景。

【讨论】:

    【解决方案5】:

    我认为这有助于退后一步,看看这里的大局。当您按照您描述的方式运行 Java 程序时,它是在虚拟机中运行的。一个恰好名为“java”的特定名称。

    不过,还有其他运行 Java 的 VM。较新的 VM 之一是 GraalVM。我不确定将其称为 JVM 是否完全正确,因为它(据说)还可以运行其他语言,例如 Python、Ruby、C 和 C++。所以如果你在 GraalVM 中运行一个 C++ 程序,那个 C++ 程序现在是否“只是”一个 GraalVM 应用程序?我不这么认为。更进一步,GraalVM 可以将 Java 程序编译为原生二进制文件。

    顺便说一句,Java 在运行时环境方面并没有什么特别之处。 C# (.NET) 有 CLR,当然,它绝对绝对不是基于 JVM 的想法。 CPython 有一个名为“python”的运行时。

    如果我在 Linux 上运行的虚拟机中运行 Windows,并且在其中运行用 C++ 编写的程序,那么 Windows 现在是否只是在 Linux 上运行的程序?如果我们说是,那么 C++ 应用程序是什么?它是一个独立的程序吗?在云服务器上运行的虚拟机上的容器中运行的 C++ 应用程序又如何呢?与在桌面上运行时相比,在该配置中运行的程序是否更不“真实”?

    TLDR:虚拟化在计算中无处不在。标准 JVM 的某些方面肯定与其他虚拟化技术不同,但这些在总体方案中是相当小的区别。

    【讨论】:

      【解决方案6】:

      当你调用java Main.classinside cmd.exe 控制台时,它真的是它自己的独立程序吗?

      没有。

      感觉更像java 是正在运行的程序,而 Main.class 只是一个给定的参数。

      是的。

      这与任何其他命令行调用没有什么不同:首先是程序名称,然后是参数。

      Java 通常不会提前完全“编译”;它是半编译的,当您想要运行程序时,结果由 Java 虚拟机执行。使用名为java 的可执行文件调用JVM。

      文件Main.class 本身并不是您的操作系统可以运行的可执行文件。

      【讨论】:

        【解决方案7】:

        免责声明:我没有 Windows 机器,所以这里是 Linux 的现状。

        一切都非常简单。以下是了解正在发生的事情的方法:

        我。

        $ which java
        /usr/bin/java -> /etc/alternatives/java*
        

        (这是针对Debian 的Linux 风格,例如Ubuntu。其他类似。)

        二。

            $gdb /etc/alternatives/java
            (gdb) list main
            93          __initenv = _environ;
            94
            95      #else /* JAVAW */
            96      JNIEXPORT int
            97      main(int argc, char **argv)
            98      {
            99          int margc;
            100         char** margv;
            101         int jargc;
            102         char** jargv;
        

        这里你看到一个简单的C main function 接受命令行参数你传递它们(虽然参数是一个复杂转换的主题)。 每次调用 Java 程序时都会调用该函数。

        它充当代理 loads libjvm.so 包含 HotSpot 代码和 calls the specific CreateJavaVM 函数将控制权传递给首先 initializes all the VM subsystems 的 HotSpot VM 代码(JIT 编译器、GC、生成解释器模板、安装信号处理程序等...) 然后打电话给你的public static void main Java function

        简而言之,您调用一个常规的本地编译二进制文件,该二进制文件知道如何执行您指定为参数的 Java 程序;)

        【讨论】:

          【解决方案8】:

          当然。这就是现代计算机的美妙之处:代码就是数据。

          在 1940 年代早期的计算机时代,“编程”计算机意味着连接电线、重新配置物理硬件。一个突破性的进步是von Neuman machine,其中程序被存储为数据,然后计算机读取该数据并根据该数据的内容采取不同的行动。

          今天所有的程序都被当作数据来操作。当你用 C# 编写程序时,它只是一堆文本字符串。然后你运行一个“编译器”来读取这些文本字符串并输出机器语言,可能是你运行编译器的处理器可以理解的语言。但不一定:有“交叉编译器”,您可以在机器 X 上编译程序以在机器 Y 上运行。(这在发明新计算机时特别有用。否则,我们将使用什么语言为新计算机编写编译器计算机 Y,什么时候还没有任何编译器可以在 Y 上运行?)

          您肯定会定期将程序文件从一台计算机复制到另一台计算机或从同一计算机上的一个文件夹复制到另一台计算机。您将获得包含程序文件的目录列表。等等。您将它们视为数据。

          所以今天基本上有三种语言:编译语言、解释语言和虚拟机语言。

          使用编译语言,您输入的程序被翻译成可以直接运行的机器代码。

          对于解释型语言,例如 BASICs,解释器会读取您的源代码并即时确定如何处理它。

          使用 Java 等虚拟机语言,您的程序被翻译成“字节码”。然后这个字节码被“虚拟机”读取和处理。基本上,字节码就像虚拟计算机的机器语言:没有(必然)可以直接执行字节码的计算机,但是我们编写了一个解释器来处理它并给出与有这样一个“真正的”机器语言。

          字节码的一个优势,也是 Java 首次推出时的主要卖点之一,就是一旦您在计算机上实现了虚拟机,它就可以运行用该语言编写的任何程序。你甚至不需要重新编译。你只需运行它。因此,如果明天有人发明了一台 Fwacbar 2020 计算机,它带有一些与 Pentium 或任何现有 CPU 完全不同的全新指令集,并且他们为该计算机编写了一个 Java 虚拟机,他们可以在其上运行任何 Java 程序。

          编写 Java 程序的人不需要为新计算机重新编译。他们不必将源代码提供给任何人。他们甚至不必知道新计算机的存在。他们的 Java 程序只能在新计算机上运行。 (当然,假设 JVM 没有错误,但你可以这么说。)

          Java 人以“一次编写,随处运行”的口号进行营销。我曾经在 Windows 上写过一个 Java 程序,也在 Linux 上测试过。但是有一个 Mac 的人买了一份副本,他能够在他的 Mac 上运行它,而我只需要一点帮助就可以正确安装它。

          【讨论】:

          • 不错的答案。我要补充一点,即使是机器代码指令也不能准确定义它的运行方式; CPU 微码和分支预测可以看作是进一步的解释步骤。
          • @tucuxi 确实如此。另一个级别的复杂性。
          • @tucuxi 虽然这在技术上是正确的,但这些东西在很大程度上对程序员是隐藏的。我们认为的机器代码(以及下一步:汇编)实际上是 CPU 提供的基本构建块。如果我要求 CPU 将“累加器”的值乘以另一个值并存储,那么重要的是乘法运算的发生并不完全是如何完成的。 --- 如果我理解正确,CPU 微码只是从 CPU 可以独立执行的较小子指令构建这些基本块。这与汽车中的“加速”没有什么不同。
          • @JeremyHarton 直到,有时,CPU 内部的工作方式出于性能原因突然变得非常重要。这个网站上有很多questions & answers 有很好的例子
          • @JeremyHarton 当然。作为一名程序员,我在很大程度上可以忽略我的代码是被解释还是编译的问题。在现代 Java 中,有时会编译代码,有时会解释代码。在过去的美好时光里,有 BASIC 解释器和 BASIC 编译器在相同的源代码上运行。我说“在很大程度上”是因为严肃的程序员应该考虑性能影响,但在“它是否工作”的层面上,这并不重要。
          【解决方案9】:

          除了其他答案之外,以这种方式表述它可能会很有用:

          很少有程序(在 PC 上)仅运行纯机器指令,并且仅依赖于硬件。大多数情况下,它们是引导加载程序,它们(最终)启动某种操作系统。

          操作系统通常允许您将程序编译为机器指令并使用它们,它要求您的程序的机器代码符合特定的模板 em>(EXEELF 等二进制文件格式),特定的操作系统可以识别,作为与该模板保持一致的回报,操作系统实际上“知道”如何运行它, 提供了许多程序可以调用和使用的资源和库(网络、文件系统访问等)。

          没有操作系统,用户空间程序将无法启动。这就是机器指令编译程序通常只能在其目标操作系统上使用的原因。

          Bytecode 编译系统让你半途而废。将程序翻译成机器代码的部分工作是在编译期间完成的。但结果不是通常操作系统(Windows、Linux 等)支持的格式。它是一个字节码,需要操作系统之上的额外环境才能实际运行您的代码。 Java 是其中一种语言。

          字节码的好处是它通常可以在任何具有适当字节码环境的机器上使用而无需更改。有点像“编译一次,到处运行”的系统,但也有一些注意事项。

          最后,我们有了您的口译语言,这要求您在每次启动程序时启动整个语言解释器,并且每次都由解释器完成所有工作。您的程序的源代码通常随时可供检查和更改,并且无需(有时很慢)重新编译过程即可完成更改。此外,在不同架构的机器上使用时,解释语言程序通常不需要修改。

          正如你想象的那样,每次启动程序所需的环境越多,程序的响应速度可能就越慢。然而,对于现代计算机和现代口译员来说,许多情况下的差异并不像以前那么显着。尽管如此,对于许多资源密集型程序来说,编译为可执行机器代码(OS 级别)仍然是首选,有时甚至是程序能够以可接受的方式运行的唯一方法。

          另外,正如一些答案和 cmets 所指出的,这三种模式之间的界限已经模糊,一些解释器做字节码,一些机器已经开发出来可以直接理解字节码。尽管如此,了解运行哪种代码通常需要什么仍然很有用。

          【讨论】:

            【解决方案10】:

            这是一个视角问题:

            • 从shell的角度来看,java.exe是程序,Main.class是一个参数。

            • 从操作系统的角度来看,进程首先运行java.exe,读取主类,生成新的本机代码,将其添加到自身,并花费大部分时间执行生成的代码(所以从这个角度来看,Java 程序不是“在顶部”,而是与 JVM “并排”)。

            • 从 Java 程序员的角度来看,Main.class 是程序,JRE 是在特定平台上执行该代码所需的运行时环境。

            值得注意的是,JRE 的加载方式具有很大的灵活性:

            • 您可以运行java.exe 将JVM 加载到新进程中。
            • 任何进程都可以加载jvm.dll 以将JVM(和Java 程序)嵌入到自己的进程中。例如,LibreOffice Base 加载 jvm.dll(或 jvm.so,取决于平台)来托管 java 程序 HSQLDB。从操作系统的角度来看,该进程将包含来自 LibreOffice Base、JVM 的代码以及为 HSQLDB 运行时生成的代码。

            总而言之,Java 程序是否“只是 java.exe”取决于您的观点,以及 Java 程序的启动方式。由于同一个程序可以以多种不同的方式启动,并且可以从不同的角度考虑,“Java 程序”通常不是“shell 命令”、“本机可执行文件”或“进程”的同义词。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2013-11-24
              • 1970-01-01
              • 2023-03-08
              • 1970-01-01
              • 2019-05-17
              • 1970-01-01
              • 2021-04-21
              • 1970-01-01
              相关资源
              最近更新 更多