22.0.0

(2021-01-18)

平台更新

  • 停止支持 Java 8。从此版本及后续版本开始,GraalVM Enterprise 发行版将仅基于 JDK 11 和 17。
  • 移除对 JDK 12、13、14、15 和 16 版本的支持。

  • GraalVM Community Edition 所基于的 OpenJDK 版本已更新至
  • GraalVM Enterprise Edition 所基于的 Oracle JDK 版本已更新至

Java 和编译器更新

  • 新增循环旋转优化,可将更多非计数循环转换为计数循环(仅在 GraalVM Enterprise 中可用)。计数循环是编译器使用的一种抽象,用于确保循环的迭代限制是一个有界整数。当循环被计数时,它变得适合部分展开、向量化和其他优化,以提高性能。例如,JDK 17 中 LinkedList.toArray() 的以下代码
    public Object[] toArray() {
      Object[] result = new Object[size];
      int i = 0;
      for (Node<E> x = first; x != null; x = x.next)
        result[i++] = x.item;
      return result;
    }
    

    将被编译器优化为

    public Object[] toArray() {
      Object[] result = new Object[size];
      int i = 0;
      Node<E> x = first;
      if (x == null) return result;             // Loop condition 1
      while (true) {
        if (i |>=| result.length) deoptimize(); // Loop condition 2
        result[i++] = x.item;
        x = x.next;
        if (x == null) break;                   // Loop condition 1
      }
      return result;
    }
    

    对于以 LinkedList.toArray() 为中心的微基准测试,循环旋转带来了略高于 10% 的性能提升。对于包含大量形状相似的非计数循环的工作负载,已测量到高达 30% 的性能改进。循环旋转在 22.0 版本中默认禁用,可以通过 -Dgraal.LoopRotation=true 启用。

  • 改进了 Graal 编译器对执行配置文件(execution profiles)的处理。(仅在 GraalVM Enterprise 中可用)。Graal 被设计为一种积极的 JIT 编译器,它严重依赖于底层运行时(HotSpot VM 或 Native Image)收集的配置文件(请参阅此处此处)。编译器利用这些配置文件来确定哪些分支是重要的、循环执行的频率以及多态代码中使用的类型。也就是说,编译器依赖于配置文件来确定优化工作的重点。因此,配置文件的质量对于内联、复制和向量化等优化至关重要。编译器现在自动切换到“AOT”(Ahead-of-Time,预编译)模式,在此模式下,即使没有配置文件,主要优化仍能正常工作。这有助于解决以下情况
    • 不会分析仍可能成为热点的非常见模式的 Truffle 语言。
    • 没有 PGO 的 Native Image。

    对于缺少良好配置文件、且循环和类型检查较多的基准测试,我们观察到 GraalVM Enterprise 的性能提升高达 25%。此优化始终启用,无法禁用,因为它是编译器的核心。它对用户是透明的,在没有精确分支和循环配置文件的情况下,应能提供更好的通用代码。

  • 新增一项优化,以提高 Native Image 中类型切换(即一系列级联的 instanceof 分支)的性能。例如

      void foo(Object o) {
        if (o instanceof A) {
          // ...
        } else if (o instanceof B) {
          // ...
        } else if (o instanceof C) {
          // ...
        }
      }
    

    通过新的优化,对 o 的类进行的空值检查和加载操作被提取出来

      void foo(Object o) {
        if (o != null) {
          Object nonNullO = o;
          Class oClass = nonNullO.getClass();
          if (A.class.isAssignableFrom(oClass)) {
            // ...
          } else if (B.class.isAssignableFrom(oClass) {
            // ...
          } else if (C.class.isAssignableFrom(oClass)) {
            // ...
          }
        }
      }
    

    只有当 Native Image 将 ABC 的子类视为已分配时,此优化才能发挥作用。否则,instanceof 测试将简化为对 o 类的 == 比较。

GraalVM Community Edition 中完整的编译器更改列表可在编译器变更日志中找到。

Native Image

  • 新增更新以减小镜像大小:堆栈帧元数据采用更紧凑的编码,可减小所有镜像的大小。此外,String.format() 的优化实现使得像“Hello World”这样的小型镜像无法访问本地化类,并显著减小了它们的大小。注意:String.format 优化仅适用于 GraalVM Enterprise。
  • GraalVM 22.0 将是最后一个允许 native-image 构建器与 JDK 8 一起执行的版本。无需显式检查并允许 JDK 12、13、14、15、16 版本,因为它们已不再受支持。建议将 Native Image 与 GraalVM JDK 11 或 JDK 17 结合使用。
  • native-image 引入了新的用户友好型构建输出,带有进度条和更多关于原生镜像构建过程的摘要信息。之前的输出可以通过 -H:-BuildOutputUseNewStyle 恢复。
  • 默认情况下,为串行垃圾回收器(Serial GC)启用了一项新的垃圾回收策略。这将垃圾回收(GC)时间和/或应用程序的 RSS 大小减少了高达 30%。串行 GC 算法是小堆的良好选择。
  • 改进了对 Java 平台模块系统(Java Platform Module System)的支持:native-image 现在支持 --add-reads--add-modules 选项。此外,所有与模块相关的选项,例如 --add-reads--add-exports--add-opens,现在都在扫描类路径/模块路径之前应用。这确保了模块在类加载之前正确配置,以防止类加载错误。更多关于模块的信息被添加到镜像堆中,这允许在运行时进行更多的模块内省。
  • 新增对 JDK 17 上密封类(sealed classes)的反射内省支持:Class.isSealed()Class.getPermittedSubclasses()
  • 移除了对 JLine 2 的支持。Native Image 历史上曾包含 JLine 2 的替代项。由于 JLine 2 不再维护,因此已将其支持移除,且没有替代方案。
  • 移除了通过 JSON 文件注册替代项的 -H:SubstitutionFiles=... 选项。
  • 更新了 C 到 Java 的互操作性:从 C 到 Java 的 @CEntryPoint 入口点的自定义序言、尾声和异常处理方法必须使用 @Uninterruptible 进行注解。它们总是被视为不可中断的,但现在必须显式标记。
  • 改进了Native Image API:添加了 CEntryPoint#include 属性,可用于控制入口点是否应自动添加到共享库。Native Image API 现在也支持 Windows AARCH64 平台。

JavaScript

完整的更改列表可在变更日志中找到。

Ruby

  • 更新至 Ruby 3.0.2,请参阅 #2453。Ruby 3 的大部分更改已在此版本中实现,Ractor、解析器更改和关键字参数更改除外。
  • TruffleRuby 现在需要 Java 11 或更高版本,不再支持 Java 8。
  • 更新了 Regexp 对象,使其以类似于符号(symbols)的方式进行字符串驻留(interned)。因此,所有 Regexp 实例都将被冻结。
  • 新增了各种优化,以使解释器更快(在代码进行 JIT 编译之前)。

完整的更改列表可在变更日志中找到。

Python

  • 新增了对pyexpat 模块的支持,这是一个用于快速非验证 XML 解析的 Python 模块,也称为 expat
  • 实现了 _csv accelerator 模块,用于以 CSV 格式读写表格数据。
  • 改进了与 PyPI 包(如 wheelclickujson)的兼容性。

项目变更日志可在 GitHub 上找到。

R

  • 改进了内部基础架构、与 Truffle 框架的兼容性以及对流行 R 包的支持。
  • 采用了新的NodeLibrary,替代了此版本中已移除的旧 API。它提供了访问与特定节点位置相关联的客语(guest language)信息的能力。这有助于 GraalVM 工具开发人员更好地支持 R 语言。

项目变更日志可在 GitHub 上找到。

LLVM 运行时

  • 切换到新的Truffle Frame API
  • 优化了循环计数,以便在第一层编译代码中也报告配置文件。这通过使循环密集型方法更快地从第一层编译过渡到第二层编译,从而改进了预热(warmup)时间。
  • 新增了一个修复,如果在库搜索路径中已加载具有相同“soname”的库,则可正确重用库依赖项。这允许用户依赖于无法在库搜索路径中自动找到的库,方法是首先手动加载它们。

项目变更日志可在 GitHub 上找到。

WebAssembly

Truffle 上的 Java

  • 通过采用新的Frame API,改进了与 Truffle 框架的兼容性。
  • 在类重定义期间,新增了对更多类更改的支持,即字段和类访问修饰符的更改。使用 --java.ArbitraryChangesSupport=true 标志启用。

多语言嵌入

  • 引入了多语言隔离区(Polyglot Isolates)的第一个版本。您可以通过调用 Context.Builder.option("engine.SpawnIsolate", "true") 为每个 EngineContext 生成一个原生镜像隔离区。这使得宿主和客语应用程序之间能够进行堆隔离。使用隔离区可以提高 Truffle 语言的安全性、启动和预热时间,并为客语应用程序提供强大的内存隔离。在此模式下,宿主和客语之间的调用成本更高,因为它们需要跨越原生边界。建议在此模式下使用 HostAccess.SCOPED 策略,以避免宿主和客语之间出现强循环引用。此模式在此版本中是实验性的,并且仅支持 JavaScript。(仅在 GraalVM Enterprise 中可用)。

项目变更日志可在 GitHub 上找到。

Truffle 语言和工具实现

  • com.oracle.truffle.api.frame.Framecom.oracle.truffle.api.frame.FrameDescriptor 添加了新的 API
    • Frame 中新增了一个基于索引槽的“命名空间”,它在帧描述符构建期间定义,之后无法更改。
    • Frame 中新增了第二个槽“命名空间”(称为辅助槽),它可以动态添加到帧描述符中(并且仅支持“对象”槽)。
    • 除了 get.../set... 方法,新 API 还支持帧槽的 copyswap 操作。
    • FrameSlotTypeException 现在是一个未经检查的异常,这简化了许多 API 并消除了对 FrameUtil 类的需求。
  • 引入了共享层。共享层是一组语言实例,它们在一个或多个多语言上下文中共享代码。在以前的版本中,每当创建新的语言上下文时,语言实例都会单独共享。相反,现在仅当整个层可以共享时,语言实例才会为新上下文重用。如果其所有已初始化语言都支持相同的上下文策略并且其选项兼容,则可以共享一个层。注意可观察到的语言行为的一些变化
    • 对于任何执行的 Truffle 节点,现在可以假定当前的语言实例将保持不变。
    • 现在,TruffleLanguage.initializeMultipleContexts() 保证在同一语言实例的所有已创建上下文之前调用。
    • 如果初始化新的语言上下文且该语言与当前上下文的共享层不兼容,则语言初始化将失败。
    • 完整的行为更改列表可在变更日志中找到。在javadoc中阅读更多关于代码共享的信息。
  • 引入了Truffle 退出 API,以支持跨多语言退出。它提供了一种统一的方式让语言触发退出。触发时,首先使用 TruffleLanguage.exitContext(C,ExitMode,int) 通知所有已初始化的客语,然后停止所有上下文线程,最后关闭上下文。有关更多详细信息,请参阅文档
  • TruffleLanguage.finalizeContext(Object) 中新增了一项要求,即在所有上下文仍处于活动状态的线程上,语言创建的所有剩余未关闭的内部上下文必须被留下。TruffleLanguage.finalizeContext(Object) 返回后,不允许存在活动的内部上下文。不符合此要求将导致内部错误。请注意,非活动的内部上下文仍由父上下文隐式关闭。
  • 改进了 engine.TraceCompilationengine.TraceCompilationDetails 的输出格式。有关详细信息,请参阅文档
  • 新增了 --engine.TraceCodeSharing 选项,允许记录代码共享的调试信息。
  • 新增了 --engine.ForceCodeSharing--engine.DisableCodeSharing 选项,用于强制启用和强制禁用代码共享。这些选项对于测试以在进程的所有上下文中启用或禁用共享非常有用。

完整的更新列表可在变更日志中找到。

工具

VS Code 扩展

  • 新增了项目浏览器(Project Explorer),它提供了逻辑项目结构的概览,将源代码分组,并极大地简化了 Java 包结构的探索。项目浏览器是对传统工作区资源管理器的补充。使用它来构建、测试、执行和操作您的 Maven 和 Gradle Java 项目。

    VS Code: Project View

  • 为 VS Code 的 GraalVM 扩展引入了多项重构和改进,包括

    • 为更改方法签名重构提供了图形用户界面

      VS Code: Refactor UI

    • 能够组织源文件的导入,并附带其他多项设置

      VS Code: Settings for Organize Imports

    • 能够生成 equals()hashCode() 方法

  • 新增了大纲视图(Outline View),提供有关已打开 Java 文件的所有详细信息
  • 新增了为每个 VS Code 实例运行独立的 NetBeans Language Server 的能力,使用其自己的类路径和 JDK。使用新选项 userdir = global | local

    VS Code: NetBeans Language Server

  • 新增了使用 SDKMan 管理 GraalVM 安装的功能
  • 通过性能和可靠性改进,以及运行独立 Groovy 脚本文件(不含 Spock 测试)的能力,改进了 Groovy 语言支持。
  • 更新了 Micronaut Gradle 插件以使用官方的 GraalVM Native Image 构建插件。因此,Micronaut: Build Native Image… 操作现在调用 nativeCompile 作业而不是 nativeImage

VisualVM

  • 新增了使用 JFR 事件进行精确线程状态监控的功能。请参阅 #363

    VisualVM: Exact Threads

  • 新增了对 JDK 18 的初步支持。

联系我们