Spring Boot + GraalVM 本地镜像使用入门指南(1)

大家好,通过本系列文章,我们将研究 Spring Native with GraalVM。本系列将有两篇不同的文章。在我们的第一篇文章中,我们将从理论上研究 Spring Native、GraalVM、提前 (AOT)、即时 (JIT)。在我们的第二篇文章中,我们将分享我们如何通过性能指标和实现过程在 Trendyol 中实现 Spring native 的经验。

Spring Native 确保 Spring 程序可以编译为本地可执行文件。为了获得这些原生可执行文件,Spring Native 使用 GraalVM Native Image 编译器来编译原生镜像。

什么是GraalVM?

GraalVM 是一个 Java 开发环境,使开发人员能够编写和执行 Java 代码。它是由 Oracle 开发的 Java 虚拟机 (JVM) 和 Java 开发工具包 (JDK)。它是一种高性能运行时,可提高应用程序的性能和效率。

GraalVM 的目标是开发更快且可维护的编译器,优化在 JVM 上运行的语言的性能,减少应用程序启动时间,将多语言支持集成到 Java 生态系统中,并提供一组编程工具来实现这些目标。

GraalVM 使用优化编译器增强了 JDK,该编译器优化了各个语言的性能并确保了多语言应用程序的互操作性。除了 Java,GraalVM 还支持以下编程语言:Kotlin、JavaScript、Node.js 和 Python。它使开发人员能够在单个应用程序中高效地运行以多种语言和库编写的代码。

在深入了解 Spring Native 之前,最好先简要了解一下 Java 应用程序的工作原理,然后了解 AOT 和 JIT 编译器是什么,因为这些主题是我们理解 Spring Native 概念的重要因素。

java是如何工作的?

为了理解 AOT 和 JIT,首先,我们需要了解 Java 代码是如何工作的。

假设我们有某种 Java 代码,您将首先使用 javac 将其编译为字节码,该字节码是代码的中间表示,它提供了可移植性,您可以在不同类型的体系结构中运行相同的字节码。你可以在 Intel 上使用它,它是一个 x86 架构,你可以在 arm 上运行它,你有一些其他的架构,比如 power 9、SPARC 等。当你运行这个字节码时,你的应用程序运行在 JVM 上,JVM 将转换你的字节码转换成机器码,这一步也叫编译。

image.png

字节码到机器码(编译)

当您的应用程序启动时,它可以访问字节码,它会一次一行,并使用某种字典,它决定了字节码在正在运行的系统上工作所需的转换类型。在 JVM 内部有一个称为解释器的组件,它将使用字典一次一行地遍历字节码,它将字节码转换为机器的指令,然后将这些指令发送给 CPU 执行。

image.png

这是字节码到机器码的基本转换。然而,这个流程存在一个问题,解释器在将字节码的每一行转换为机器码时,即使是重复的代码,如 for 循环或应用程序经常使用的方法,也会浪费时间编译你的代码。因此,在没有任何优化的情况下使用这个过程是非常低效的。此时,C1 和 C2 编译器正在加入该过程。

C1编译器

JVM 会记录哪些方法或哪些代码片段执行了多少次。例如,如果有一个名为 sumTwoNumbers 的方法,并且每当调用此函数时,该特定函数的计数器都会增加。一旦该计数器达到特定阈值,JVM 就会意识到该方法将被多次使用,因为它已经达到了阈值。然后C1编译器会把超过阈值的函数取出来编译然后保存到JVM的一个叫做code cache的区域。所以下次当这个方法被调用而不是用解释器再次编译时,它会从代码缓存中提取编译后的代码,并将这些指令交给 CPU 运行。在这个过程的帮助下,复杂化过程得到了优化。

image.png

C2编译器

在后台,JVM 将开始收集有关代码执行方式的运行时统计信息,并尝试创建控制流图或代码的核心部分,以查找代码中最常用的部分。一旦 JVM 有足够的统计信息,它将要求 C2 编译器执行更重的优化,就像 C1 编译器编译的代码将保存在代码缓存中一样,如果相同的方法/代码找到代码缓存,它将用 C2 替换 C1 的方法。

image.png

AOT 和 JIT

C1 和 C2 编译器不会阻止应用程序运行,因此它会根据课程的数量决定为 C1 和 C2 创建多少后台线程,并且当代码在幕后被解释时,编译可以继续。一旦编译好的代码准备好,JVM 将从核心缓存中执行代码而不是询问解释器,所以所有这些编译和解释都是在应用程序启动和运行时在运行时完成的,这就是为什么叫JIT即时编译。

另外,JVM 可以在将 Java 代码转换为字节码时执行此操作,这称为AOT提前编译,从 Java 9 开始,我们可以选择在运行应用程序之前将您的代码或库的某些部分转换为已编译的代码。

Spring Native

Spring native 为用户提供了构建应用程序的本机映像的功能,该映像可供开发人员免费使用。在单个包中,这些本机映像包含您的代码、库、资源和 JDK 的所有功能,并且它们已经过优化以在特定平台上执行。此过程的好处是更少的内存和 CPU 使用率,以及应用程序启动时间的减少。由于系统开销减少和垃圾收集周期减少,启动更快的应用程序消耗更少的内存和 CPU。

AOT 编译器支持也已作为 0.9 版本引入 Spring Native,现已可用。使用 Spring Native,现在我们可以在我们的系统中利用 AOT 编译,我们在本文的前一节中讨论过。

创建原生镜像并不是一个新系统,它是一个已经使用了很长时间的概念,尤其是大型企业公司,因为它通过在云和容器上进行优化,在可扩展性、效率和快速应用程序启动时间方面具有优势。

GraalVM 虚拟机希望在其应用程序中使用原生想法在 Java 开发人员中特别受欢迎。然而,他们会遇到问题,尤其是当他们想同时使用 Spring 和 GraalVM 时。最严重的问题是 Spring 程序中的依赖注入;使用 GraalVM 将 Spring 启动应用程序转换为原生映像非常困难,特别是由于各种 spring 组件之间的连接。由于 GraalVM 主要依赖于静态分析,因此很难理解 Spring 的依赖注入和自动布线模型。

这就是 Spring Native 发挥作用的地方; Spring Native 旨在通过建立一个系统来轻松高效地生成与 GraalVM 兼容的原生镜像来解决这个问题。

支持的技术

由于 Spring Native 仍然是一个新的开发中的概念。它并不支持我们在 Spring 中使用的所有技术。但是,支持的技术数量随着每个新版本的增加而增加,包括:

  • spring-boot-starter-data-jdbc
  • spring-boot-starter-data-jpa
  • spring-boot-starter-actuator
  • spring-boot-starter-data-elasticsearch
  • spring-boot-starter-data-mongodb
  • spring-boot-starter-data-redis
  • spring-boot-starter-security
  • spring-boot-starter-test

除了我们上面写的之外,Spring Native 还支持许多技术。如果您想查看所有支持的技术,可以使用此链接

我们的文章到此结束。总的来说,我试图提供有关 Spring Native 和 GraalVM 问题的理论信息。在此之后,您可以阅读我们文章系列的第二部分。在第二部分中,有关我们如何编译 Spring 本机应用程序及其性能指标的详细信息等着您。


链接:Spring Boot + GraalVM 本地镜像使用入门指南(1) - 掘金