IntelliJ IDEA Java

Java 运行时:Spring Boot 角度的见解

Read this post in other languages:

为您的 Spring Boot 项目选择合适的 Java 运行时乍看可能十分简单。 毕竟,所有运行时都基于 OpenJDK 代码并提供相同的 API。

但并非所有运行时都生来平等。 本文将探讨可能影响您为 Spring Boot 应用程序选择特定 Java 发行版的决定的各种指标。

本文由 JetBrains IntelliJ IDEA 的团队主管 Aleksey Stukalov 和客座作者 Catherine Edelveis(BellSoft 的技术布道师)撰写

开发者的视角

功能兼容性

对于大多数开发者来说,使用哪个 Java 发行版并无太大区别。 无论您是在开发 Spring Boot 应用程序还是根本不使用框架,这都无关紧要。 为什么? 关键在于技术兼容性套件 (TCK)。

JDK 本身只是一个表面,下面隐藏着大量不同的规范。 为了确保 JDK 实现能够正确运行您的 Java 应用程序,该实现通过称为 TCK 的一套庞大测试集进行验证。 通过这些测试的实现被称为“经 TCK 验证的发行版”,这会让开发者充分相信他们的应用程序在所有经 TCK 验证的 Java 运行时中以相同方式运行。

虽然经 TCK 验证的发行版保证了代码的相同功能行为,但可能存在其他差异,后面会详细讨论。

工具支持

基于上述信息,开发者似乎没有偏爱的 JDK 发行版,但事实并非如此。 我们来看看为什么某些选项优于其他选项。

从供应商网站下载 JDK 的日子已经一去不复返了。 现在,只需安装您的首选 Java IDE,就能确保您拥有必要的运行时。 如果是 IntelliJ IDEA,默认 JDK 将是 JetBrains Runtime,它是 OpenJDK 的一个复刻,提供了几个增强功能,如用于实现无缝代码更新的高级类重定义功能 (DCEVM),以及一个可选的 JCEF(用于嵌入基于 Chromium 的浏览器的框架)。 虽然这些附加功能可能不在每个开发团队的要求列表中,但 JetBrains Runtime 被嵌入到工具中并作为默认选项,这一点使其成为 Java 开发者中最常用的 JDK 之一。

虽然 JetBrains Runtime 是 IntelliJ IDEA 中的默认选项,但您可以一键下载多个 JDK。 Amazon Corretto、Azul Zulu、BellSoft Liberica JDK(标准版或带有 JavaFX)、Eclipse Temurin、GraalVM CE 或 Oracle GraalVM、IBM Semeru 和 SapMachine 都可以在 IDE 中获得,也同样受开发者欢迎。

Spring Boot 特性

Spring Boot 提供了一种便捷的方式来帮助将应用程序转化成容器镜像,无需编写 Dockerfile(构建包)。 不过,并非所有 Java 运行时都可以用于此目的。 Spring Boot 使用的 Paketo 构建包提供了几种 JDK 发行版作为容器镜像的基础。 默认选择为 Liberica JDK,这也是 Spring 官方推荐的 Java 运行时。 您也可以使用其他 JDK,但请记住,有些只提供 JDK 版本,这意味着最终的容器镜像将比基于 JRE 的镜像占用明显更多的内存。

如果您使用 Paketo 构建包,在开发时使用相同的 JDK 也有意义。 尽管所有经 TCK 验证的发行版在功能上是兼容的,但在调试内存泄漏、性能问题和其他问题时,使用不同的 JDK 仍然会遇到困难。

DevOps 视角

当您接近上线时,拥有一个 Spring Boot 应用程序在决策中变得更加重要。 现代服务最终都走向了云端,其中 Kubernetes (K8s) 已成为事实上的标准。 因此,我们将此设置作为目标。

调整 K8s 的默认设置

使用 Kubernetes 在云端部署应用程序特别轻松,因为它默认为部署和集群编排提供了良好的设置。 然而,调整默认设置可以为公司节省大量成本,特别是对于内存需求较大的复杂 Spring Boot 应用程序。 下面是一些提高 Kubernetes 中 Java 应用程序性能的建议:

  • 负载测试和压力测试旨在评估应用程序在稳定状态和峰值负载下的内存需求,根据这两项测试的结果设置 K8s pod 的 CPU 和 RAM 限制。
  • 选择合适的垃圾回收器。 除了热门的 ParallelGC 和 G1GC,最新的 Java 版本还包括诸如 ZGC 和 ShenandoahGC(后者不随 Oracle Java 一起提供)之类的新垃圾回收实现,专为大堆实现尽可能减小的延迟而设计。
  • 使用 Kubernetes 探针监测您的 pod 的运行状况,并在出现问题时能够及时反应。

在容器中使用 JRE

容器镜像越小,实例占用的内存越少,因此云费用也越低。 此外,如果贵公司使用自己的 Docker 注册表,随着时间的推移,它往往会越来越臃肿,因此在这种情况下,节省内存也很重要。

容器化的 Spring Boot 应用程序可能会变得相当庞大。 为了让它们保持简洁,您可以选择带有 JRE 和轻量级 Linux 发行版(如 AlpineAlpaquita)的合适基础镜像。

下表汇总了 Docker Hub 上提供的 JDK 21 的基础容器镜像。 我们比较了不同 JDK 供应商提供的使用基于 musl 或 glibc 的操作系统层的压缩 JDK 和 JRE 镜像大小(评估中未包括 Distroless 镜像,因为它们可能不是某些用例的最佳选择)。 数据收集时间为 2024 年 4 月 23 日。 

除了 Liberica JDK 和 Red Hat OpenJDK,所有镜像都基于 Alpine Linux(适用于 musl)或 Ubuntu(适用于 glibc)。 Liberica JDK 镜像基于 Alpaquita Linux,它与 Alpine 完全兼容,但有两种版本:基于 musl 和基于 glibc。 Red Hat OpenJDK 镜像基于 RHEL UBI 9。

Docker Hub 上未提供 Oracle Java 构建。

  Liberica JDK Amazon Corretto Azul Zulu IBM Semeru Runtimes Eclipse Temurin Red Hat OpenJDK
JDK + 基于 glibc 的操作系统 85.63 MB 212.66 MB 191.89 MB 260.52 MB 197.87 MB 148.5 MB
JRE + 基于 glibc 的操作系统 52.97 MB -* 106.97 MB 96.44 MB 92.82 MB 126.7 MB
JDK + 基于 musl 的操作系统 78.02 MB 155.72 MB 157.66 MB 167.05 MB
JRE + 基于 musl 的操作系统 45.37 MB -* 72.43 MB 62.5 MB

* Amazon Corretto 的 JRE 镜像不在 Docker Hub 上提供,但可以在 AWS 上获得。 

升级到最新的 Java

得益于 JVM 的众多改进,较新的 Java 版本的性能显著高于旧版本的性能。 这意味着升级 Java 版本对于跟上云端应用程序性能的现代要求至关重要。

此外,Spring Boot 3.x 要求使用 Java 17 作为基线,因此如果您想利用您最喜欢的框架的新主要版本的强大功能,升级 Java 版本至关重要。

不过,如果现在不能升级,一些供应商(如 Oracle 和 BellSoft)也提供了将 JVM 17 与 JDK 8 结合的解决方案(对于 JDK 11,BellSoft 也有类似的解决方案)。 这样会使您的应用程序认为它仍然运行在旧的 Java 版本上,但实际上它拥有一个较新的引擎。 对您来说,这意味着什么? 无需升级 Java 或框架,也几乎无需进行代码更改,即可立即缩短延迟并提高吞吐量!

最大限度缩短启动时间

复杂的 Spring Boot 应用程序可能需要几十秒才能启动。 即使是小型参考应用 Spring Petclinic 也需要 5 到 7 秒! 此外,Java 应用程序在预热期间消耗的资源比在稳定状态时需要的更多。 这会导致以下问题:

  • 在预热期间和更大的实例上浪费额外的 CPU 周期预算。
  • 由于服务延迟较高,用户满意度下降。
  • 无法充分利用 AWS Lambdas 或类似服务的潜力。

幸运的是,Spring Boot 3 支持两种最有效的解决方案来处理长时间预热的问题 – GraalVM 原生镜像和检查点协调恢复 (CRaC) 项目。 您只需要选择一家为此功能提供支持的 Java 供应商。

检查点协调恢复 (CRaC) 

借助 CRaC,您可以暂停一个正在运行的 Java 应用程序,将其保存到文件,然后从暂停的那一刻起从文件恢复,从而高效地将启动时间从几秒缩短到几毫秒。

Spring Boot 从 3.2 版本开始与 CRaC 集成。 目前有两家 OpenJDK 供应商提供支持 CRaC 的 JDK 构建:Azul 和 BellSoft。 Azul 仅开发 JDK,并依赖 Ubuntu 提供 CRaC 支持。 BellSoft 提供了带有他们自己的 Alpaquita Linux 和 Liberica JDK 的支持 CRaC 的容器镜像,因此您可以在 Docker 容器中使用 CRaC,而无需调整 JDK 和 Linux 或向应用程序添加除 org.crac 依赖项之外的额外配置。

CRaC 项目尚未通过 TCK 验证。 如果您正在寻找可付诸生产的功能,请考虑使用原生镜像技术。

原生镜像

原生镜像技术使开发者能够在封闭世界假设下预先将 Java 应用程序转换为静态原生镜像。 原生镜像的启动几乎是瞬时的,并且占用的内存更少,因为它们不包括 JDK。

除了将原生镜像作为 Oracle GraalVM 的一部分交付的 Oracle 之外,还有两家 JDK 供应商将 native-image 编译器作为其 Java 产品的一部分提供:

  • Red Hat 的 Mandrel 专为 Quarkus 框架量身定制。
  • BellSoft 的 Liberica 原生镜像套件用作 Spring 构建包中的默认 native-image 编译器,并获得了 Spring 团队的推荐。 它基于 GraalVM CE,但包括在 GraalVM Community Edition 中缺少的 ParallelGC。

SecOps 视角

随着网络攻击在数量和攻击性方面不断增长,每年保持高水平的项目安全性越来越具有挑战性。 为了确保您的 Java 应用程序安全,您必须密切关注依赖项和更新。

使用软件物料清单

软件物料清单 (SBOM) 对于软件供应链安全不可或缺。 它通过提供有关组件版本、许可证和已知 CVE 的数据来帮助您监视所有运行时依赖项的状态。

现在,您可以方便地使用 Maven 和 Gradle 插件为您的项目创建 SBOM,以便您的客户了解依赖项的状态。 不过,也应该对开发中使用的解决方案充满信心,包括 Java 运行时。 对于保持基础架构安全而言,供应商提供 SBOM 的 Java 运行时是一个非常好的选择。

为您用于部署的容器镜像生成 SBOM 也很重要,因为如果您不了解基础镜像的内部情况,就无法在它们感染已知 CVE 时及时反应。 而这反过来会导致您的生产环境极易受到攻击。 您可以使用现有工具(如 Docker Scout)生成 SBOM,但从供应商那里获得现成的 SBOM 更加方便。

定期更新 Java

接收包含安全补丁和关键补丁的季度构建的 Java 运行时有助于保持您的应用程序基础免受已知 CVE 的影响。

主要 JDK 供应商提供季度 PSU 版本,其中的三家(Oracle、BellSoft 和 Azul)还会发布面向 LTS 版本的稳定 CPU 构建以及带有补丁和仅关键修正的最新功能版本。 CPU 构建使您能够在不破坏生产环境的情况下引入安全补丁。

季度版本根据 LTS 版本的支持路线图发布,但 LTS 版本的支持生命周期因供应商而异,下表中对此进行了汇总。

  Oracle JDK Liberica JDK Amazon Corretto Azul Zulu IBM Semeru Runtimes Eclipse Temurin* Red Hat OpenJDK
较旧 Java 版本的商业支持结束 
JDK 6 2026 年 3 月 2027 年 12 月
JDK 7 2026 年 3 月 2027 年 12 月
JDK 8 2030 年 12 月 2031 年 3 月 2026 年 5 月 2030 年 12 月 2026 年 11 月 2026 年 11 月
JDK 11 2026 年 9 月 2032 年 3 月 2027 年 9 月 2032 年 1 月 2026 年 11 月 2024 年 10 月
较新 Java 版本的支持期
JDK 17+ 8 年 8.5 年 7 年 8 年 6 年 6 年

对 Eclipse Temurin 的支持由第三方供应商提供。

结论

在 Spring Boot 开发中使用哪个 Java 运行时可能并不重要,只要它经过 TCK 认证即可。 不过,诸如性能、容器镜像范围、有效云部署的附加解决方案等重要指标使某些运行时更适合您的 Spring Boot 项目。

下表总结了我们在本文中探讨的所有内容,概述了可以帮助开发 Spring Boot 的主要功能以及可以提供这些功能的 Java 运行时。

功能 Oracle JDK Liberica JDK Amazon Corretto Azul Zulu IBM Semeru Runtimes Eclipse Temurin Red Hat OpenJDK
TCK 验证 + + + + + + +
Spring 推荐 +
多个安装程序和软件包(tar、deb、MSI、DMG、JDK/JRE) 无 apk,无 pkg,无 JRE + 无 dmg + + + 无 dmg,无 deb,无 apk,无 rpm,无 pkg
软件包管理器、Docker 镜像、从 IntelliJ IDEA 直接安装 + + + + + IDE 中未提供
带有 JavaFX 的软件包 + 对于版本 8,仅适用于特定平台  + +
用于 Spring Boot 的 Paketo 构建包 仅 JDK 版本 17+ +(默认) 仅 JDK + +
Alpine Linux 支持 + + + + +
带有 JRE 的最小基础镜像 45.37 MB 72.43 MB 96.44 MB 62.5 MB 126.7 MB
GraalVM 原生镜像编译器 作为 Oracle GraalVM 的一部分 Liberica 原生镜像套件(用于 Spring Boot 的默认 native-image 编译器) 适用于 Quarkus 的 Mandrel
CRaC 支持 + +
LTS 版本的免费更新生命周期 3 年 8.5 年 7 年 8 年 6 年 6 年 6 年
稳定的 CPU 构建 + + +
SBOM + + + + + + +
提高 Spring Boot 2.x 项目性能的解决方案 +(适用于 JDK 8) +(适用于 JDK 8 和 11)
提供商业支持 + + 仅适用于 AWS + + 并非来自 Adoptium +

您使用哪个 JRE 来开发和部署您的 Spring Boot 应用程序? 在下方评论中告诉我们吧。

本博文英文原作者:

image description

Discover more