Blog chevron_right 未分类

剖析 JVM

Java 不仅仅是一种编程语言。它是一套“全覆盖的体系”,涵盖了多种工具、运行时,甚至一整个社区。在这篇文章中,我们将着眼于这套“全覆盖的体系”下的一个组成部分:Java 虚拟机。Java 虚拟机究竟是什么,它如何运作?


Java 在最受欢迎的语言排行榜中一直名列前茅。这背后的真正原因并不在于语言本身,而在于 JVM,以及它赋予开发人员的强大能力。

Azul 副首席技术官 Simon Ritter


理解 JDK、JRE 和 JVM 之间的差异

在软件领域,各种缩写词汇纷繁复杂,可能会引发混淆,甚至导致特定术语被误用。因此,我们首先需要明确以下几个术语的区别。

理解 JDK、JRE 和 JVM 之间的差异

JDK:Java 开发工具包

JDK 位于 Java 生态系统的最顶端,是其他所有元素的“根源”。每当 Java 更新版本时,都会推出全新版本的 JDK,其中将包含各种改进、错误修复和安全补丁,并且会新增或移除一些工具。JDK 包含以下几个组成部分:

  • JVM=Java 虚拟机:可执行的 java,用于运行您的应用程序。其工作原理将在下文详述。
  • 用于开发 Java 应用程序的,由 5000 多个类组成,包含 java.util、java.text、java.nio、java.sql…
  • 一套帮助您创建、监控和运行 Java 应用程序的工具,例如:
    • javac:用于将 Java 类编译成字节码
    • javadoc:基于您的 Java 源文件生成包含 API 文档的 HTML 页面
    • jshell:在 Shell 终端中与 Java 代码交互并执行 Java 代码
    • 等等。

JRE:Java 运行时环境

在 Java 9 之前,每个 JDK 都具有配套的 JRE,它包含与 JDK 相同的 JVM 和库,但只提供有限的工具集。因此,它可以视作 JDK 的子集。JRE 的设计目标是,使执行应用程序的设备无需承担所有工具的开销。此外,JRE 的文件更小,便于下载和分发。然而,Java 9 引入了模块,允许您使用 jlink 构建运行时,只在其中包含运行应用程序所需的模块,而且,您创建的运行时将比 JDK 或 JRE 小得多。

由于许多组织仍依赖于使用 JRE 的方法,Azul 及其他提供商依然会为新版本创建 JRE。请访问 Azul 核心下载页面,查看所有可用 JRE 的列表。

JVM 内部机制

正如我们所了解到的,Java 虚拟机 (JVM) 是 JDK 的一部分,它负责运行我们的应用程序,被称为“托管式运行环境”。  在这个术语中,“托管式”是关键,因为它意味着,JVM 不仅执行代码,还能在此基础上处理大量额外功能。JVM 在应用程序和运行应用程序的机器之间建立了中间层。

一次编写,处处运行

这是 Java 在诞生时就提出的承诺。作为开发人员,您只需编写一套代码库,即可在任意平台上执行您的程序,不受操作系统(Windows、Mac、Linux 等)和硬件架构(x86、ARM、aarch64 等)的限制。为此,您需要将 Java 代码转换为字节码。这是开发人员的职责,您可以通过 Maven 或 Gradle 等构建工具,利用 javac 完成这项任务。这些字节码将通过 JVM 在机器上执行,这意味着您可以在任何平台上运行任何类!

为了实现这一目标,必须针对各个受支持的操作系统和平台,将 JVM 字节码“转译”成其所需的特定原生代码。这意味着,为了能够支持不同的平台,作为开发人员的您本来需要编写所有不同的实现,而现在,Java 系统的开发者已经替您处理了这一切。您可以查看源代码,了解如何在 Java 项目中实现这一点。所有这些代码都可以在 GitHub 上免费获取。例如,通过这个链接,就可以找到 HotSpot 的不同实现,如下面的项目截图所示。

即时编译

应用程序启动时,字节码将通过 JVM 中的模板模型直接转换为平台指令。此时,尚未执行任何优化,Java 的运行速度将慢于功能相似的原生编译代码。然而,与此同时,JVM 会立即追踪每个方法的调用频次。一旦达到预定义的阈值(也被称为 HotSpot),JVM 内部的即时 (JIT) 编译器将开始分两个阶段执行工作:

  • C1 JIT:首次转换为原生代码。
    • 一旦方法被检测为 Hotspot,其代码就会被重新编译为原生代码。
    • 这一过程会尽快完成,仅提供最低限度的优化,一旦这些原生代码得到使用,JVM 会再次对其做出分析,了解其使用情况。
  • C2 JIT:尽可能地实现最佳优化。
    • 同样,当达到特定阈值时,JVM 将再次重新编译,生成原生代码,但为了达到最佳性能,这次编译将花费更多时间,因为需要将分析纳入考虑范围,以实现最佳性能。
    • 此时,生成的原生代码将完美地符合代码的适用情况。
    • 这意味着,根据运行环境、需要处理的数据、触发代码执行的事件等因素,相同的代码可以产生不同的原生代码。
    • Azul Zulu Prime 包含一种 C2 的替代方案:Falcon JIT。它基于开源项目 LLVM,在大多数情况下,与传统 C2 相比,其生成的代码性能更出色。如需更多信息,请参阅 Azul 文档中的《使用 Falcon 编译器》

观察下方图表,我们可以看到,从解释型字节码(黄色)转换到最初的优化代码(C1,绿色),再到在最终优化代码(C2,蓝色)中达到最佳性能,应用程序的速度是如何提升的。如需关于这个主题的更多信息,以及如何“调整”应用程序,以改进这些步骤,请参阅《分析和调整的预备知识》。

JIT 与预先 (AOT) 编译相反,生成的原生代码针对具体任务进行优化后,表现通常更佳。

如果想深入了解字节码转换为原生代码的过程,可以阅读 Simon Ritter 的以下博客文章: 

内存管理

JVM 的另一项职责是自动执行内存管理。C、C++ 等其他编程语言需要开发人员分配 (malloc) 和释放内存,如果处理不当,将导致出现内存问题。垃圾回收器 (GC) 是 JVM 中专门为您处理该问题的组件。它让开发人员彻底摆脱了管理内存使用的烦恼。GC 会定期查找代码中不再需要且未被引用的对象,并释放内存空间,以便重新利用。

您可以在此处找到专门介绍垃圾回收器的文章:《作为 Java 开发人员,我应该了解垃圾回收的哪些知识?》

What Should I Know About Garbage Collection as a Java Developer?

线程管理

线程管理以及线程间的交互也由 JVM 负责处理。

OpenJDK 19 中包含 Project Loom 和 Virtual Threads 的首个评估版本,在 Java 内部引入了独立于操作系统线程的多级线程。这将为 Java 中的线程使用方式带来新变革。通过与操作系统线程数量保持独立,将会大幅提升效率,并对可扩展性以及流式传输或消息传递应用程序产生重要影响。

如需更多信息,请访问此处:《JDK 19 以及 Java 用户应了解的其相关信息》

静态与动态之间的差异

我们经常听到人们使用另一个术语“静态类型语言”描述 Java。这里的“静态”是指变量的使用方式。声明新值时,您需要定义一个类型,该类型在之后将无法更改。例如,使用 String label = “Hello, World!” 进行声明后,您将无法为变量 label 指定数字类型的值。这一点与 JavaScript 之类的语言不同。具有动态类型语言背景的新手 Java 程序员可能会对此感到困惑,但这种方式能在执行代码时预防许多问题和错误。
请记住,“静态”与值的定义方式相关,因为类正好相反,它是以“动态”方式在运行时加载的!这允许应用程序根据环境或特定设置,正确地发挥作用。例如,基于环境设置,您可以在测试和生产中使用不同的数据库。在每一种情况下,都可以动态地加载其他类,与数据库交互。

“Azul 版 OpenJDK”是什么?

所有 Java 运行时都必须以相同的方式发挥作用,确保用户无论使用何种 JDK 发行版,其应用程序都将产生相同的结果。为了实现这一点,每个发行版都必须符合 Java SE 规范,该规范由 Java Community Process (JCP) 的相关 Java 规范提案 (JSR) 定义。

要验证是否符合规范,发行版必须通过 Java 技术兼容性工具包 (TCK) 的所有测试。虽然大多数发行版都基于 OpenJDK,但这并不意味着所有发行版都是以相同的方式来实现的!Azul 提供了两个发行版,二者都通过了 TCK 测试,但又又有很大差别。

Azul Zulu 版 OpenJDK(又名 Zulu)

  • 此 OpenJDK 版本几乎与原版 OpenJDK 完全相同
  • 它没有任何功能上的变化。
  • 但是,它包含对旧版本的错误修复和安全改进。举例而言,Azul 是唯一仍在创建新版本 JDK 7,并将对安全性和错误的修复反向移植到其中的发行商。
  • Azul 为所有版本提供 JRE 发行版。
  • Azul 也在持续更新和维护几乎不被所有发行商支持的版本(例如JDK 7)。
  • Azul 以以下方式提供:
    • Azul Zulu 版 OpenJDK
      • 可直接替代任何 OpenJDK。
      • 可免费下载和使用。
    • Azul Platform Core:
      • 在 Azul Zulu 版 OpenJDK 的基础上,额外提供支持、升级和附加工具。
      • 提供用于评估和开发的免费版本,以及授权版本。

Azul Prime 版 OpenJDK(又名 Prime)

  • • 该发行版基于 OpenJDK。
  • • 但是,它做出了一些功能上的更改:
    • • 移除了 OpenJDK 垃圾回收器的所有实现,将其替换为 Azul C4 垃圾回收器。
    • • 虽然没有移除 C2 JIT 编译器,但添加了 Azul Falcon 编译器,您可以通过启动选项选择要使用的编译器。
  • • JDK 内部增加了以下额外功能:
    • • Connected Runtime Service (CRS),可以连接到 Azul 漏洞检测,帮助实时识别安全问题。
    • • ReadyNow,一种使 Java 应用程序可以快速启动并保持快速运行的技术。
  • • 在 Azul Platform Prime 中提供:
    • • 在 Azul Prime 版 OpenJDK 的基础上,额外提供 C4、Falcon、ReadyNow、支持、升级和附加工具。
    • • 提供用于评估和开发的免费版本,以及授权版本。

Azul JDK 发行版可通过“下载 Azul JDK”页面下载。

结语

与其他语言相比,JVM 处理了开发人员不希望处理,也不需要处理的许多任务。这将有助于您专注于业务逻辑和实际的实施工作