读书笔记《supercharge-your-applications-with-graalvm》第 3 章 GraalVM 架构
Chapter 3: GraalVM Architecture
在 第 1 章中,Java 虚拟机的演进,我们详细了解了 JVM 架构。在 第 2 章中,JIT、HotSpot和 GraalJIT,我们更详细地介绍了 JVM JIT 编译器的工作原理。我们还研究了 JVM 如何演变为具有 C1 和 C2 JIT 编译器的最佳 HotSpot VM。
虽然 C2 编译器非常复杂,但它已成为一段非常复杂的代码。 GraalVM 提供了一个 Graal 编译器,它构建在 C2 编译器的所有最佳实践之上,但它完全是用 Java 从头开始构建的。因此,Graal JIT 更加面向对象,并且具有现代且可管理的代码,并支持所有现代集成开发环境、工具和实用程序来监视、调整和管理代码。 GraalVM 不仅仅是 Graal JIT 编译器。 GraalVM 引入了一个更大的工具、运行时和 API 生态系统,以支持多种语言 (Polyglot) 在 VM 上运行,利用 Graal 提供的最成熟和最强化的 JIT 编译。
在本章中,我们将重点介绍 GraalVM 架构及其各种组件,以实现最先进、最快的多语言运行时云。我们还将探索云原生架构模式,以及 GraalVM 如何成为云的最佳平台。
在深入了解 GraalVM 架构的细节之前,我们将首先了解现代技术架构的要求。在本章后面,当我们浏览每个 GraalVM 架构组件时,我们将解决这些要求。
在本章中,我们将介绍以下主题:
- Reviewing modern architectural requirements
- Learning what the GraalVM architecture is
- Reviewing the GraalVM editions
- Understanding the GraalVM architecture
- An overview of the GraalVM microservices architecture
- An overview of various microservices frameworks that can build code for GraalVM
- Understanding how GraalVM addresses various non-functional aspects
在本章结束时,您将对 GraalVM 架构以及各种组件如何组合在一起为多语言应用程序提供全面的 VM 运行时有一个非常清晰的了解。
Reviewing modern architectural requirements
在深入研究 GraalVM 架构之前,让我们先了解一下 JVM 的缺点,以及为什么我们需要新的架构和方法。较旧版本的 JVM 针对传统架构进行了优化,这些架构专为在数据中心运行的长时间运行的应用程序而构建,提供高吞吐量和稳定性(例如,单体 Web 应用程序服务器和大型客户端应用程序)。有些微服务是长期运行的,Graal JIT 也会提供最优的解决方案。随着我们转向云原生,整个架构范式已转变为组件化、模块化、分布式和异步架构,这些架构经过调整以高效运行,具有高可扩展性和可用性要求。
Smaller footprint
这些应用程序由粒度模块化组件(微服务)组成,以实现高可扩展性。因此,构建占用空间较小的应用程序非常重要,这样它们就不会消耗过多的 RAM 和 CPU。随着我们转向云原生部署,这一点变得更加重要,因为我们在云上p按使用次数选择。占用空间越小,我们可以在云上用更少的资源运行更多。这对关键业务 KPI 之一的总拥有成本 (TCO) 有直接影响。
更小的占用空间还有助于我们快速、持续地进行更改和部署。这在敏捷世界中非常重要,系统的构建是为了迎接变化。随着业务的快速变化,应用程序也需要快速接受变化以支持业务决策。在传统的单体架构中,即使是很小的更改也需要整体构建、测试和部署。在现代架构中,我们需要灵活地以模块化方式推出功能更改,而不会使生产系统停机。
我们有新的工程实践,比如A/B测试,我们将这些功能模块(微服务)的测试与旧版本并行进行,来决定新版本是否足以推出。我们执行金丝雀部署(滚动更新),更新应用程序组件,而不停止生产系统。我们将在本章后面的DevOps - 持续集成和交付部分更详细地介绍这些架构要求。
Quicker bootstrap
可扩展性是最重要的要求之一。现代应用程序被构建为根据负载快速扩展和缩减。负载呈指数增长,现代应用程序需要优雅地处理任何负载。由于占用空间更小,这些应用程序组件(微服务)也有望快速启动以开始处理负载。随着我们转向更多无服务器架构,应用程序组件有望根据请求处理启动和关闭。这需要非常快速的启动策略。
更快的引导和更小的占用空间也对使用嵌入式 VM 构建应用程序组件提出了挑战。基于容器的方法要求这些应用程序组件是不可变的。
Polyglot and interoperability
多语言是的现实:每种语言都有自己的优势并将继续具有,所以我们需要接受这个事实。如果你看一下解释器/编译器的核心逻辑,它们都是一样的。他们都试图达到类似的优化水平,并生成运行速度最快的机器代码,占用空间最小。我们需要的是一个最佳平台,可以运行这些各种应用程序,用不同的语言编写,并且允许它们之间的互操作性。
考虑到这些架构要求列表,现在让我们了解 GraalVM 的工作原理以及它如何满足这些要求。
Learning what the GraalVM architecture is
GraalVM 提供了一个 Graal JIT 编译器,它是 JVMCI(我们在上一章中介绍过)的实现,它完全建立在 Java 之上,并使用 C2 编译器优化技术作为基线并在其之上构建。 Graal JIT 比 C2 编译器复杂得多。 GraalVM 是 JDK 的直接替代品,这意味着当前在 JDK 上运行的所有应用程序都应该在 GraalVM 上运行,而无需更改任何应用程序代码。
虽然 GraalVM 基于 Java 构建,但它不仅支持 Java,还支持使用 JavaScript、Python、R、Ruby、C 和 C++ 进行多语言开发。它提供了一个名为 Truffle 的可扩展框架,允许在平台上构建和运行任何语言。
GraalVM 还提供 AOT 编译来构建带有静态链接的原生镜像。 GraalVM 附带 以下运行时、库和工具/实用程序列表(这是 GraalVM 20.3.0 版本。最新的组件列表可以在 https://www.graalvm.org/docs/introduction/。)
现在我们已经了解了 GraalVM 中的组件,我们将了解可用的 GraalVM 的各个版本,以及这些版本之间的差异。
Reviewing the GraalVM editions (Community and Enterprise)
GraalVM 可作为社区版和企业版使用:
- Community Edition: GraalVM Community Edition (CE) is an open source edition built as an OpenJDK distribution. Most of the components of GraalVM are GPL 2, with a classpath exception licensed. For more details on licensing, please refer to https://github.com/oracle/graal#license. GraalVM CE is based on OpenJDK 1.8.272 and OpenJDK 11.0.9. GraalVM CE is community supported. It can be deployed in production. However, it does not come with the required support services from Oracle. Oracle also provides a Docker image, which is readily downloadable, for testing and evaluation (refer to https://www.graalvm.org/docs/getting-started/container-images/ for further details).
- Enterprise Edition: GraalVM Enterprise Edition (EE) is a licensed version under the GraalVM OTN license agreement. This is free for evaluation and building non-production applications. GraalVM EE provides additional performance (~20% faster than CE and dynamic languages such as JavaScript, Python, R, and Ruby are ~2x faster), a smaller footprint (~2x smaller than CE), security (native code memory protection), and scalability for running production enterprise applications. EE comes with additional debugging tools, such as Ideal Graph Visualizer, which helps in not only debugging performance issues, but also in fine-tuning the applications for best performance on GraalVM. GraalVM EE comes with support services. For Oracle cloud customers, GraalVM EE support is available as part of the subscription. GraalVM EE also has a managed mode, which does better heap management, avoiding page faults and crashes. GraalVM EE is available for clients who already have a Java SE subscription. The benefit of EE is that there are patented optimizations that provide additional performance boost that varies depending on the workload, Profile Guided Optimization and a better G1GC garbage collector for an additional performance boost.
对于 Oracle 云客户,GraalVM EE 支持作为订阅的一部分提供。 GraalVM EE 还有一个托管模式,可以更好地管理堆,避免页面错误和崩溃。 GraalVM EE 适用于已经订阅 Java SE 的客户。 GraalVM Enterprise 可以下载并在 Oracle 云实例中免费使用。 GraalVM Enterprise 可在 Oracle Cloud Ampere 1 计算实例上使用,其成本/性能远低于在 x86 云实例上运行的成本/性能。现在我们知道了 GraalVM 的各种可用版本,以及它附带的运行时、工具和框架,让我们深入了解 GraalVM 架构的细节。
Understanding the GraalVM architecture
在本节中,我们将了解 GraalVM 的各种架构组件。我们将了解各种运行时、工具和框架如何组合在一起以提供最先进的 VM 和运行时。下图展示了 GraalVM 的高级架构:
图 3.1 – Graal VM 架构
让我们详细介绍这些组件中的每一个。
JVM (HotSpot)
JVM HotSpot 是 常规 Java HotSpot VM。 C2 编译器是 HotSpot VM 的一部分,已替换为 Graal JIT 编译器实现。 Graal JIT 编译器是 Java 虚拟机编译器接口 (JVMCI) 的实现,并插入到 Java VM 中。我们在前面的章节中介绍了 JVM HotSpot 的架构。请参阅它们以更深入地了解 JVM HotSpot 的工作原理以及 JVM 的各种架构组件。
Java Virtual Machine Compiler Interface (JVMCI)
JVMCI 是在 Java 9 中引入的。允许将编译器编写为 JVM 可以调用以进行动态编译的插件。它提供了一个 API 和一个 协议来构建具有自定义实现和优化的编译器。
在这种情况下,compiler 一词表示即时编译器。在前面的章节中,我们详细介绍了 JIT 编译器。 GraalVM 使用 JVMCI 来访问 JVM 对象,与 JVM 交互,并将机器代码安装到代码缓存中。
Graal JIT 实现有两种模式:
- libgraal: libgraal is an AOT compiled binary that is loaded by HotSpot VM as a native binary. This is the default mode and the recommended way to run GraalVM with HotSpot VM. In this mode, libgraal uses its own memory space and does not use the HotSpot heap. This mode of Graal JIT has quick bootup and improved performance.
- jargraal: In this mode, Graal JIT is loaded like any other Java class, and hence it goes through a warm-up phase and runs with an interpreter until the hot methods are identified and optimized. This mode can be invoked by passing the --XX:-UseJVMCINativeLibrary flag from the command line.
在 OpenJDK 9+、10+ 和 11+ 中,我们使用 -XX:+UnlockExperimentalVMOptions、-XX:+UseJVMCICompiler 和 XX:+EnableJVMCI< /strong> 标志来运行 Graal 编译器,而不是 C2 编译器。 GraalVM 默认使用 Graal JIT 编译器。始终建议使用 GraalVM 发行版,因为这些 具有最新的更改。 OpenJDK 以较慢的速度合并更改。
在下一章中,我们将使用示例代码详细介绍 Graal JIT 如何优于 C2 JIT。我们将使用 Graal 附带的调试工具和实用程序来演示 Graal JIT 在运行时执行的优化。
Graal compiler and tooling
Graal 编译器建立在 JVMCI 之上,并提供了一个更好的 JIT 编译器(我们在前两章都介绍过的 C2)实现, 进一步优化。 Graal 编译器还提供了一个 AOT (Graal AOT) 编译选项来构建可以与嵌入式 VM 一起独立运行的原生镜像。
Graal JIT compiler
我们在 第 1 章中查看了 JVM 架构, Java 虚拟机。作为参考,这里是 JVM 的高级架构概述:
图 3.2 – 带有 C2 编译器的 JVM 架构
如您所见,C1 和 C2 编译器将 JIT 编译作为 JVM 执行 引擎的一部分来实现。我们详细介绍了 C1 和 C2 如何根据编译阈值优化/反优化代码。
GraalVM 取代了 JVM 中的 JIT 编译器,并进行了进一步的优化。下图展示了 GraalVM 的高级架构:
图 3.3 – 带有 Graal 编译器的 VM 架构
JVM JIT 编译器和 Graal JIT 之间的区别之一是 Graal JIT 是为了优化中间代码表示(抽象语法树(AST)并使用 Graal 图或 Graal 中间表示)。 Java 在编译时将代码表示为 AST,即中间表示。
任何语言表达式和指令都可以转换并表示为 AST;这有助于从优化代码的逻辑中抽象出特定于语言的语法和语义。这种方法使 GraalVM 能够优化和运行以任何语言编写的代码,只要代码可以转换为 AST。我们将在第 4 章,Graal Just-In-Time Compiler 中深入研究 Graal 图和 AST。
- Profiler: As the name suggests, it profiles the running code and generates the information that is used by the code optimizer to take decisions or make assumptions regarding optimization.
- Intermediate Code Generator: This generates the intermediate code representation, which is the input for the code optimizer.
- Code Optimizer: This uses the data that is collected by profiles and optimizes the intermediate code.
- Target Code Generator: The optimized code is then converted to the target machine code.
下图显示了 Graal JIT 如何在非常高的级别上工作:
图 3.4 – Graal JIT 编译 – 高级流程图
- The JVM language (Java, Kotlin, Groovy, and so on) code runs on Graal JIT natively, and Graal JIT optimizes the code.
- The non-JVM languages (such as JavaScript and Ruby) implement a language parser and interpreter using the Truffle API. The language interpreters convert the code into AST representation. Graal runs the JIT compilation on the intermediate representation. This helps in leveraging all the advanced optimization techniques implemented by Graal to non-JVM languages.
- The native LLVM-based languages (such as C/C++, Swift, and Objective C) follow a slightly different route to convert to the intermediate representation. Graal Sulong is used to create the intermediate representation that is used by Graal. We will be talking about Truffle and Sulong later in this chapter.
Graal JIT optimization strategies
Graal JIT 优化策略是从头开始构建的,基于 C2 JIT 编译器优化策略的最佳实践。 Graal JIT 建立在 C2 优化策略之上,并提供更高级的优化策略。以下是 Graal JIT 编译器执行的一些 优化策略:
- Partial escape analysis
- Improved inlining (http://aleksandar-prokopec.com/resources/docs/prio-inliner-final.pdf)
- Guard optimization (http://lafo.ssw.uni-linz.ac.at/papers/2013_VMIL_GraalIR.pdf)
- Chaining lambdas
- Inter-procedural optimization
在下一章中,我们将借助示例代码和示例详细介绍这些优化策略。
Truffle
Truffle 框架是一个开源库,用于构建解释器和工具/实用程序(例如集成开发环境、调试器、和分析器)。 Truffle API 用于构建可以在 GraalVM 上运行的语言解释器,利用 GraalVM 提供的优化。
Graal 和 Truffle 框架由以下支持 Polyglot 的 API 组成:
- Language Implementation Framework: This framework is used by the language implementers. It also comes with a reference implementation of a language called SimpleLanguage. We will be going through this in detail in Chapter 9, Graal Polyglot – LLVM, Ruby, and WASM.
- Polyglot API: This set of APIs aids interaction between code written in different languages (guest languages) with Java (the host language). For example, a Java (host) program can embed R (guest) language code to perform some machine learning/AI logic. The Polyglot API provides the framework that will help the language programmers to manage the objects between the guest and the host.
- Instrumentation: The Truffle Instrumentation API provides the framework for utilities/tool builders to build integrated development/debugging environments, tools, and utilities. The tools and utilities that are built using the Truffle Instrumentation API can work with any language that is implemented with Truffle. This provides a consistent developer experience across various languages and leverages the sophisticated debugging/diagnostic capabilities of JVM.
图 3.5 展示了 Truffle 如何充当 GraalVM 和其他语言解释器之间的中间层的高级架构。各个语言解释器是使用 Truffle API 实现的。 Truffle 还提供了一个互操作性 API,用于在各种语言实现中调用方法和在方法之间传递数据:
图 3.5 – Truffle 架构
如上图所示,Java 应用程序直接在 GraalVM 上运行,而 Graal 编译器 取代了 C2 JIT 编译器。其他语言程序在 Truffle 语言实现框架之上运行。各自的语言解释器使用 Truffle 来实现解释器。 Truffle 将代码与解释器结合起来,使用部分评估生成机器代码。
AST 是中间表示。 AST 提供了表示语言句法结构的最佳方式,其中通常父节点是运算符,子节点表示操作数或运算符(基于基数)。下图显示了 AST 的粗略表示:
图 3.6 – 用于简单表达的 AST
在此图中,a、b 和 c 可以是任何变量(对于松散类型的语言)。解释器根据各种执行的分析开始假设“泛型”。然后它开始假设细节,然后使用部分评估优化代码。
Truffle(用 Truffle 编写的语言解释器)作为解释器运行,Graal JIT 开始识别代码中的优化。
优化 是基于推测,最终,如果 推测在运行时被证明是不正确的,JIT 将重新优化并重新编译(如上图所示)。重新优化和重新编译是一项昂贵的任务。
部分评估从代码和数据创建语言的中间表示,当它学习和识别新的数据类型时,它对 AST 解释器进行反优化,应用优化,并进行节点重写和重新编译。在某一点之后,它将具有最佳表示。下图解释了 Truffle 和 Graal 如何优化中间表示:
图 3.7 – Graal JIT 的 AST 优化
- The expression is reduced to an AST. In the AST nodes, the leaf nodes are operands. In this example, we have taken a very simple expression to understand how partial evaluation works. In a language such as JavaScript, which is not a strongly typed language, a, b, and c can be any data type (sometimes referred to as generics). Evaluating a generic in an expression is a costly operation.
- Based on the profiling, Graal JIT speculates and assumes a specific data type (in this example, as an integer), optimizes the code to evaluate the expression for integers, and compiles the code.
- In this example, it is using an inlining optimization strategy. The Graal JIT compiler has various other optimization strategies that are applied based on the use case.
- When, during runtime, the compiler identifies a control flow where one of the operands is not really an integer, it deoptimizes and rewrites the AST with the new data type and optimizes the code.
- Following a few iterations of running this optimization/deoptimization, the compiler will eventually generate the most optimum code.
这里的关键区别在于 Graal 正在处理 AST 并生成 优化代码,使用什么语言并不重要只要代码表示为AST,就可以编写源代码。
下图展示了不同语言如何在 GraalVM 上运行的高级流程,Truffle 作为中间层,在 GraalVM 上执行任何编程语言代码:
图 3.8 – Truffle 和 Graal 编译流程图
此图说明了 Truffle 如何充当非 JVM 语言和 GraalVM 之间的层的更简单表示。代码也可以直接构建为带有 Substrate VM 的本机映像。
Truffle API 与自定义注释处理器一起使用来生成解释器源代码,然后编译。 Java 代码不需要中间的 表示。它可以直接编译运行在 GraalVM 上。我们将在第 9 章中讨论 Truffle 解释器以及如何编写自定义解释器>,Graal Polyglot – LLVM、Ruby 和 WASM。我们将在 第 6 章中介绍 Truffle Polyglot API , Truffle 支持多语言(多语言)。
Truffle 还提供了一个名为 Truffle Instrument API 的 框架,用于构建工具。 Instruments 提供细粒度的 VM 级运行时事件,可用于构建分析、跟踪分析和调试工具。最好的部分是使用 Truffle 构建的语言解释器可以使用 Truffle 工具的生态系统(例如,VisualVM、Chrome 调试器和 GraalVM Visual Studio Code Extension)。
Truffle 提供多语言互操作性协议。该协议定义了每种语言需要实现的消息,并支持多语言应用程序之间的数据传递。
Sulong – LLVM
LLVM 是一个开源项目,它是模块化、可重用编译器和工具链的集合。有很多语言(C、C++、Fortran、Rust、Swift 等)编译器构建在 LLVM 之上,其中 LLVM 提供中间表示(也称为 LLVM-IR)。
Sulong 管道看起来与我们在 Truffle 上运行的其他语言编译器中看到的不同。下图显示了 C/C++ 代码是如何编译的:
图 3.9 – LLVM 编译流程图
此图显示了如何在 GraalVM 上编译和运行用 C 编写的应用程序代码。使用 C/C++ 等本地语言编写的应用程序代码在 Clang 中编译成中间表示。此 LLVM 中间表示在 LLVM 中间表示解释器上运行,该解释器基于 Truffle API 构建。 Graal JIT 将在运行时进一步优化代码。
SubstrateVM (Graal AOT and native image)
Graal 上的应用程序可以部署在 GraalVM 或 SubstrateVM 上。 SubstrateVM 是 可嵌入的 VM 代码,在 AOT 编译期间被打包到本机映像中。
Graal AOT 编译是一种非常为特定目标操作系统/架构创建本机二进制文件的强大方法。对于云原生工作负载和无服务器,这是实现更小的占用空间、更快的启动以及更重要的是可嵌入运行时(提供不变性)的一个非常强大的选项。
快速的组件化模块化部署(容器)也带来了管理和版本控制方面的挑战。这就是通常称为配置漂移,这是我们在高可用性环境中管理大量容器时面临的主要问题之一。通常,容器基础设施由一个团队构建,并且随着时间的推移,它由不同的团队管理。在某些情况下,我们不得不在我们可能永远无法追踪的特定环境中更改虚拟机/容器/操作系统的配置。这会导致生产环境和 DR/HA 环境之间出现差距。
不可变的基础设施(图像)帮助我们对基础设施进行更好的版本控制。它还 让我们对测试更有信心,因为运行我们的应用程序容器的底层基础设施是 不可变的,而且我们是确定测试结果。要构建不可变组件,我们需要一个可嵌入的 VM(占用空间小)。 SubstrateVM 提供了可嵌入的 VM。
在AOT编译中,代码直接编译成机器码并执行。没有运行时分析或优化/反优化。 Graal AOT 编译器(也称为“本机映像”编译器)对代码执行静态分析和静态初始化,并生成嵌入 VM 的可执行代码。 AOT 执行的优化是基于代码的可达性。下图展示了编译过程:
图 3.10 – Graal AOT 编译
该图显示了 Graal AOT 如何编译原生镜像并将 SubstrateVM 作为原生镜像的一部分嵌入。 AOT 编译的缺点之一是 VM 无法像在 JIT 中那样基于运行时分析来优化代码。为了解决这个问题,我们使用配置文件引导优化策略来捕获应用程序的运行时指标,并使用该配置文件数据通过重新编译来优化本机映像。
Profile Guided Optimization (PGO)
GraalVM 使用 Profile Guided Optimization (PGO) 来根据 < /a>运行时分析数据。这是仅在企业版中可用的功能之一。下图显示了 PGO 管道的工作原理:
图 3.11 – 使用 PGO 编译 Graal AOT
- When the code is compiled with native-image, we use the --pgo-instrumented flag. This will tell the compiler to inject instrumentation code into the compiled code.
- When we start running this instrumented native image, the profiler starts collecting the runtime data and then starts creating the profile files (.ipof).
- Once we have run the native image with various workloads (all possible workloads – to capture as much instrumentation as possible), we can then recompile the native image with the --pgo flag (native-image --pgo=profile.iprof), providing the profile files as input. The Graal native image compiler creates the optimum native image.
我们将在下一章中借助真实示例构建具有配置文件引导优化的原生 图像,并了解内存管理在原生图像中的工作原理。
GraalVM 是现代微服务架构的绝佳运行时。在下一节中,我们将介绍 GraalVM 的各种有助于构建微服务应用程序的功能。
An overview of GraalVM microservices architecture
GraalVM 非常适合 微服务架构。某些微服务架构最重要的要求之一是占用空间更小,启动速度更快。 GraalVM 是在云中运行 Polyglot 工作负载的理想运行时。市场上已经有云原生框架可以构建应用程序以在 GraalVM 上以最佳方式运行,例如 Quarkus、Micronut、Helidon 和 Spring。当作为原生镜像运行时,这些框架的执行速度几乎快了 50 倍。我们将在 章节中详细介绍 GraalVM 如何成为微服务的正确运行时和平台10,GraalVM 的微服务架构。
Understanding how GraalVM addresses various non-functional aspects
在本节中,我们将介绍微服务云原生架构的典型非功能性需求,以及 GraalVM 如何满足这些需求。
Performance and scalability
性能和可扩展性是微服务云原生架构更重要的非功能性需求之一。微服务由 Kubernetes 等编排器自动扩展和缩减。这要求微服务构建在快速启动和快速运行的运行时之上,消耗最少的云资源。 GraalVM AOT 编译有助于构建与 C/C++ 等原生语言相当的原生镜像。
要了解 AOT 编译代码(原生镜像)如何比 JIT 编译代码更快,让我们看看 JIT 和 AOT 在运行时遵循的步骤:
图 3.12 – Graal JIT 与 AOT 流程图
此图显示了 JIT 和 AOT 的高级步骤。 JIT 通过在运行时分析代码在一段时间内优化代码。存在性能开销,因为 JVM 在运行时完成了额外的分析、优化和反优化。
根据 Apache Bench 基准观察,虽然一开始 GraalVM JIT 吞吐量和性能低于 AOT,但随着请求数量的增加,Graal JIT 在每秒约 14,000 个请求后优化并优于 Graal AOT。
还观察到,Graal AOT 的执行速度比 Graal JIT 快 50 倍,内存占用比 Graal JIT 小 5 倍。
具有 PGO 吞吐量的 Graal AOT 一致,有时甚至优于 Graal JIT。但是,对于长时间运行的任务,Graal JIT 可能具有更好的吞吐量。因此,为了获得最佳吞吐量和一致的性能,带有 PGO 的 Graal AOT 是最好的。
请参考 https://www.infoq.com/presentations/graalvm- 上发布的基准研究性能/ 和 https://www.graalvm.org/why-graalvm/。
在 https://renaissance.dev 上与学术合作者一起发表了进一步的基准研究。
以下是我们可以得出的结论:
- GraalVM Native Image (AOT) is best for faster startups and applications that require a smaller footprint, such as serverless applications and container microservices.
- GraalVM JIT is best for peak throughputs. Throughput is a very important aspect for long-running processes, where scalability is critical. This could be high-volume web application servers such as e-commerce servers and stock market applications.
- A combination of garbage collection configuration and JIT will help in achieving reduced latency. Latency is very important as regards the responsiveness of applications. When we are running high throughput, it's possible that on occasion, garbage collection slows down the response.
使用它没有硬性规定。这取决于我们需要在 JIT 和 AOT 之间决定 的各种组合,以及可能的各种其他配置。我们将在下一章探讨各种编译器和原生镜像配置。
Security
GraalVM 安全性建立在基于沙盒模型的 JVM 安全性之上。让我们快速回顾一下沙盒模型的工作原理:
图 3.13 – JVM 安全模型
在 Java 2 安全架构中,所有的类文件都通过字节码验证器进行验证(有关类加载器的更多详细信息,请参阅前两章)。字节码验证器检查类文件是否有效,并查找任何溢出、下溢、数据类型转换、方法调用、对类的引用等。
一旦字节码被验证,依赖的类就会被类加载器加载。请参阅第 1 章,Java 虚拟机的演进,了解类加载器子系统的工作原理.类加载器与安全管理器和访问控制一起使用,以强制执行在策略文件中定义的安全规则。检查通过网络下载的 Java 代码的签名(表示为 java.security.CodeSource,包括公钥)。
安全管理器 (java.lang.SecurityManager) 是处理授权的最重要组件。安全管理器进行了各种检查以确保完成授权。访问控制器 (java.security.AccessContoller) 类是另一个有助于控制对系统资源的访问的关键类。
Keystore 是一个受密码保护的存储,其中包含所有私钥和证书。商店中的每个条目也可以设置密码保护。
Java 安全性是可扩展的,具有 称为 Security Providers 的自定义安全性实现。
GraalVM 建立在 Java 安全模型之上,并将其抽象化以解决在中间表示级别执行安全性的问题。 GraalVM 不建议在安全管理器上运行不受信任的代码。
GraalVM 安全模型使用 Truffle 语言实现框架 API 为 JVM 宿主应用程序创建一个执行上下文,该上下文被传递给来宾应用程序(以不同语言编写的应用程序代码)。下图显示了 GraalVM 如何允许来宾和主机应用程序互操作并确定如何控制访问的高级架构:
图 3.14 – Graal 安全模型
执行上下文 (org.graalvm.polyglot.Context) 定义来宾应用程序的访问控制。根据在执行上下文中定义的访问控制,来宾应用程序可以访问系统资源。 GraalVM 提供了一个 Polyglot API 来创建这些访问控制,并带有执行上下文,以设置访问权限以访问各种功能,例如文件 I/O、线程和本机访问.根据主机设置的权限,来宾将拥有该访问权限。看门狗线程用于对上下文进行时间限制。看门狗将在给定时间内关闭上下文以释放资源,并根据时间限制访问。
以下代码演示了如何设置执行上下文:
上下文上下文 = Context.newBuilder().allowIO(true).build();
上下文上下文 = Context.newBuilder() .fileSystem(FileSystem fs) 。建造();
上下文上下文 = Context.newBuilder() .allowCreateThread(true)。建造()
上下文上下文 = Context.newBuilder() .allowNativeAccess(true)。建造()
GraalVM 还提供了一个 API 来在主机和来宾应用程序之间交换对象:
- Guest to Host Data Exchange: The guest application can call the host methods and may pass the data. However, this is controlled based on method access modifiers and the host access policy (ALL, NONE or EXPLICIT – @HostAccess.Export Annotation, for example).
- Host to Guest Data Exchange: The objects passed from the host to the guest need to be handled by guest languages. The data is passed through the context, for example:
值 a = Context.create().eval("js", "21 + 21");
主机应用程序可以将值 a 返回给 JavaScript 来宾应用程序,其值为 42(评估后)。
我们将在第 6 章中详细介绍这一点, Truffle for Multi-language (Polyglot) support,在一个真实例子的帮助下。
GraalVM EE 还为 LLVM 中间表示代码提供了一种托管执行模式,以处理任何内存违规和故障。请参考 https://docs.oracle。 com/en/graalvm/enterprise/19/guide/security/security-guide.html 了解更多详情。
DevOps – continuous integration and delivery
DevOps 自动化是任何现代云原生架构的核心要求之一。 GraalVM 很好地集成到 DevOps 管道中。下图说明了具有代表性软件的典型 GitOps 管道(GraalVM 集成到 DevOps 软件堆栈的任何堆栈中):
图 3.15 – 使用 GraalVM 的 GitOps
让我们更好地理解这个图表。
持续集成 (CI) 管道由典型触发从 Git 存储库中拉取请求,其中包含应用程序代码和基础架构代码的更改。 CI 工具(例如 GitHub 操作、Argo CD 或 CicleCI)可用于编排 CI 管道。典型的 CI 管道包括以下内容:
- Build: In the build phase, the release tagged code is pulled from the appropriate branch from the Git repository. The code is verified (any static code analysis) and built. For cloud-native, typically, the code is built as native images, using the Graal AOT compiler.
- Test: The code is tested with unit testing scripts and further verified for any security vulnerabilities.
- Package: Once the code passes all the tests, the code is typically packaged into the cloud-native target runtime (using a Docker image or VM or any other binary format). The target could be a serverless container or Docker container or a VM.
- Store: The final binaries are stored on binary stores or repositories, such as Docker Hub or Red Hat Quay (if it's a Docker image).
持续部署管道可以基于发布计划触发,也可以手动触发(取决于发布计划和策略)。持续部署通常具有以下阶段:
- Deployment for Validation: The final binary ID deployed to an environment where the binary can now be tested end to end. Various strategies can be followed:
一个。 传统上:我们有一个集成测试环境和一个用于各种的用户验收测试环境(或预生产环境) 验证和测试级别。
湾。 蓝/绿部署:有两个并行环境(称为蓝和绿)。其中一个将投入生产,让我们假设蓝色。绿色环境可用于测试和验证我们的代码。一旦我们确定新版本运行良好,我们使用路由器切换到绿色环境并使用蓝色环境测试未来的版本。这提供了一种部署应用程序的高可用性方式。
C。 金丝雀部署和滚动更新:金丝雀部署是最近的一种使用相同环境进行生产和验证的方法。这是一个很棒的功能,可以测试我们的代码并将新版本与当前版本进行比较(A/B 测试)。 Canary 部署提供 API 管理层,可用于根据各种参数(例如测试用户或特定部门的用户可以访问新版本,而最终用户仍在使用旧版本)将流量重定向到特定端点版本)。 应用程序可以部署在特定数量的服务器/节点上(按百分比或数量)。随着我们对新版本越来越有信心,我们可以通过增加新版本应该运行的节点数量来执行滚动更新,并向更广泛的用户圈开放。这也为分阶段推出新版本提供了灵活性(按地区或用户人口统计或任何参数)。
- Testing: There are various levels of testing that are performed, both functional and non-functional. Most of this is performed with automation, and that is also choreographed by the Continuous Delivery pipeline.
- Production Deployment: Once it's all tested, the final application is deployed to the production environment. Once again, this deployment may use one of the Traditional or Blue/Green or Canary strategies.
GraalVM 提供了一种非常灵活的方式来将应用程序部署为独立、容器、云、VM 和 Oracle 数据库。 非常复杂的微服务框架,例如 Quarkus、Micronaut 和 Fn 项目,它们为 GraalVM 提供原生支持,并与现代 GitOps 工具很好地集成。
Summary
在本章中,我们探索了 GraalVM 架构。 Graal JIT 是 JIT 编译器的新实现,它取代了 C2 编译器,并带来了更多的优化。 Graal JIT 完全用 Java 实现。 Truffle 提供解释器实现框架和 Polyglot 框架,以将其他非 JVM 语言引入 GraalVM。
本章很好地理解了 GraalVM 附带的各种运行时、框架、工具、Graal 更新程序和实用程序。我们还查看了 GraalVM 的两个可用版本以及这两个版本之间的主要区别。我们浏览了 GraalVM 架构的所有不同组件。我们还探讨了架构的一些非功能方面,包括安全模型、性能和 DevOps。如果您想了解如何使用 GraalVM 构建云原生微服务和跨各种语言的高性能应用程序,这一点非常重要。
在下一章中,我们将深入探讨 Graal JIT 的工作原理,我们如何使用 Graal 附带的各种工具来了解 Graal JIT 的内部工作原理,以及我们如何使用这些工具来调试和微调我们的代码.
Questions
- What are the various editions of GraalVM?
- What is JVMCI?
- What is Graal JIT?
- What is Graal AOT? How does PGO help AOT compilation?
- What is Truffle? How does it help to run multiple language codes on GraalVM?
- What is SubstrateVM?
- What is Guest Access Context?
- Why is GraalVM the ideal runtime for cloud-native microservices?
Further reading
- Lightweight Cloud-Native Java Applications (https://medium.com/graalvm/lightweight-cloud-native-java-applications-35d56bc45673)
- Java on Truffle — Going Fully Metacircular (https://medium.com/graalvm/java-on-truffle-going-fully-metacircular-215531e3f840)
- GraalVM (https://www.graalvm.org/)
- GraalVM Enterprise Edition (https://docs.oracle.com/en/graalvm/enterprise/20/index.html)
- GraalVM Git (https://github.com/oracle/graal)