Graalvm

使用 Spring Boot 和 GraalVM 构建原生镜像

1、概览 本年将带你了解原生镜像(Native Image)的相关知识,以及如何使用 Spring Boot 和 GraalVM 构建原生镜像应用。 本文使用的是 Spring Boot 3,但是在末尾会教你如何解决与 Spring Boot 2 的差异问题。 2、原生镜像 原生(本地)镜像是一种将 Java 代码构建为独立可执行文件的技术。该可执行文件包括应用程序类、其依赖项的类、运行时库类以及来自 JDK 的静态链接本地代码。JVM 被打包到原生镜像中,因此在目标系统上不需要任何 Java 运行环境,但构建产物依赖于平台。因此,需要为每个支持的目标系统进行一次构建,在使用 Docker 等容器技术时会更加简单,将容器构建为一个目标系统,可以部署到任何 Docker 运行时。 2.1、GraalVM 和 Native Image Builder 通用递归应用和算法语言虚拟机(Graal VM)是一个高性能的 JDK 发行版,专为 Java 和其他 JVM 语言编写,同时支持 JavaScript、Ruby、Python 和其他几种语言。它提供了一个原生镜像生成器(Native Image builder),这是一个从 Java 应用中生成原生代码并将其与 VM 一起打包成独立可执行文件的工具。Spring Boot Maven 和 Gradle Plugin 除了少数 例外情况(Mockito 目前不支持原生测试),正式支持该工具。 2.2、两个特性 在构建原生镜像时,会遇到两个典型特性。 Ahead-Of-Time(AOT)编译是将高级 Java 代码编译成本地可执行代码的过程。通常由 JVM 的即时编译器 (JIT) 在运行时进行编译,这样可以在执行应用程序时进行观察和优化。在 AOT 编译的情况下,这一优势就不复存在了。 通常,在进行 AOT(Ahead-of-Time)编译之前,可以选择进行一个单独的步骤,称为 AOT 处理,即从代码中收集元数据并提供给 AOT 编译器。将这两个步骤分开是有意义的,因为 AOT 处理可以是针对特定框架的,而 AOT 编译器更加通用。下面的图片给出了一个概览:

构建 GraalVM Docker 镜像

1、简介 GraalVM 使用其 Ahead-Of-Time(AOT)编译器将 Java 应用程序编译为机器可执行文件。这些可执行文件直接在目标机器上执行,而无需使用即时编译器 (JIT)。GraalVM生成的二进制文件体积较小,启动速度快,并且在没有任何预热的情况下提供最佳性能。此外,这些可执行文件相比在 JVM 上运行的应用程序而言,内存占用和 CPU 使用率较低。 通过 Docker,我们可以将软件组件打包成 Docker Image,并作为 Docker 容器运行。Docker 容器包含应用程序运行所需的一切,包括应用程序代码、运行时、系统工具和库。 在本教程中,我们将了解如何创建 Java 应用程序的 GraalVM 原生(native)镜像,以及如何将该原生镜像用作 Docker 镜像,并将其作为 Docker 容器运行。 2、原生镜像是什么? 原生镜像(Native Image)是一种将 Java 代码提前编译成原生可执行文件的技术。该原生可执行文件只包含运行时需要执行的代码。这包括应用程序类、标准库类、语言运行时和 JDK 中静态链接的本地代码。 原生镜像生成器(Native Image Builder)会扫描应用程序类和其他元数据,以创建一个特定于操作系统和体系结构的二进制文件。本地镜像工具会执行静态应用程序代码分析,以确定应用程序运行时可访问的类和方法。然后,它将所需的类、方法和资源编译成二进制可执行文件。 3、原生镜像的优点 原生镜像可执行文件有几个好处: 由于原生镜像生成器只编译运行时所需的资源,因此可执行文件的体积很小 本地可执行文件的启动时间极短,因为它们是在目标机器中直接执行的,无需使用 JIT 编译器 由于只打包所需的应用程序资源,暴漏的攻击面较小 将其打包为轻量级容器镜像(如 Docker Image),有助于快速高效地部署 4、构建 GraalVM 原生镜像 在本节中,我们将为 Spring Boot 应用构建 GraalVM 原生镜像。首先,需要安装 GraalVM 并设置 JAVA_HOME 环境变量。其次,创建一个包含 Spring Web 和 GraalVM Native Support 依赖的 Spring Boot 应用:

万剑归宗:Spring Boot 3.2、GraalVM 原生镜像、Java 21 和 Project Loom 虚拟线程

酝酿已久,我们终于可以创建使用 Spring Boot(3.2)和 Java 21 虚拟线程(Project Loom)的 GraalVM 原生镜像了! 这一切有什么意义呢?Project Loom 和 GraalVM 原生镜像各自都具有引人注目的运行时特性。我已经等了很久,终于等到了它们的融合!让我们依次唠唠。 GraalVM 原生镜像 GraalVM 是一个 OpenJDK 发行版,提供了一些额外的实用工具,其中包括一个名为 native-image 的工具,它可以对你的代码进行提前编译(AOT)。我们在这里不会详细介绍所有的实用功能,基本上它会对你的代码进行优化,去除你不需要的部分,然后将剩余的代码编译成针对特定操作系统和架构的原生代码,运行速度非常快。结果令人惊叹,类似于编译 C 或 Go 程序所得到的结果。生成的可执行文件在启动时几乎没有延迟,并且在运行时占用的内存要少得多。想象一下,能够部署现有的 Spring Boot 应用程序,并且它只占用几十兆字节的内存,并在几百毫秒内启动。现在,这是可能的。只需运行 ./gradlew nativeCompile 或 ./mvnw -Pnative native:compile,即可。自 2022 年 11 月 Spring Boot 3.0 发布以来,Spring Boot 已经支持在生产环境中使用 GraalVM 原生镜像。 Project Loom Project Loom 为 JVM 引入了透明的 Fiber(纤程,也成为协程、虚拟线程)。就目前而言,在 Java 20 或更早的版本中,IO 是阻塞的。调用 InputStream#read() 可能需要等待下一个字节的到达。在 java.io.File IO中,很少会有太多延迟。然而,在网络中,你真的无法确定。客户端可能会断开连接。客户端可能会经过一个隧道。同样,很难说。在此期间,程序流程被阻塞在执行线程上。在下面的代码片段中,我们无法知道何时会看到打印出来的单词 after。可能是从现在开始的纳秒级时间,也可能是从现在开始的一周后。它是阻塞的。 InputStream in = ... System.out.println("before"); int next = in.