Spring Boot 3.3提供CDS与Leyden支持


Spring Boot 开发人员如何以最小的限制提高其应用程序的运行时效率,以便在大多数应用程序上享受这些好处?答案是 Spring Boot 3.3 引入的 CDS 支持,它允许您更快地启动 Spring Boot 应用程序并消耗更少的内存。它基于我几个月前介绍的Spring Framework 6.1 引入的基础。

关键点是,与GraalVM 原生镜像支持相比,这种新的 CDS 支持提供了不同的价值主张:例如,在启动时,使用 CDS 获得的改进不如使用原生镜像那么显著,但它们仍然非常显著,同时您可以继续使用常规 JVM,而副作用很少。

Spring Boot 以生产就绪的方式支持 CDS 和 GraalVM 原生镜像,并根据您的情况和意见让您进行选择。

CDS,JVM 中的一颗隐藏宝石
CDS 是类数据共享,它是一项成熟的技术,已在大多数 JVM 中使用,但目前尚未发挥其全部潜力。简单来说,您可能已经在不知不觉中使用了 CDS,不过可能只是为了优化 JDK 类加载,而您的应用程序或库的类可能并未利用它。

要解锁“让应用程序或库的类利用CDS”,需要对您的应用程序进行训练运行。
您还需要满足一组约束,如果没有 Spring Boot 这样的专门支持,这些约束很容易被打破:

  • 必须使用完全相同的 JVM。
  • 类路径必须指定为 JAR 列表,并避免使用目录、*通配符和嵌套 JAR。
  • 必须保留 JAR 的时间戳。
  • 使用 CDS 存档进行生产运行时,类路径必须与创建存档时使用的类路径相同,且顺序相同。可以在末尾指定其他 JAR 或目录(但不会被缓存)。

Spring Boot 3.3 通过提供两个新功能释放了这一潜力:自解压可执行 JARBuildpacks CDS 支持

自解压可执行 JAR
直接使用可执行 JAR 运行:java -jar my-app.jar

  • 这并不是在生产环境中运行应用程序的最有效方式。这里
Spring Boot 3.3 改变了这一点,并引入了可执行 JAR 的自我提取功能,无需任何外部工具,只需使用可能已经可用的 java 命令即可运行应用程序:

java -Djarmode=tools -jar my-app.jar extract --destination application

然后,您可以使用以下方式更有效地运行 Spring Boot 应用程序:

java -jar application/my-app.jar

此功能具有超强功能:它旨在满足 CDS(和 Project Leyden)约束。因此,结合 Spring Framework 对 CDS 训练运行的支持,您可以为 Spring Boot 应用程序创建 CDS 存档,如下所示:

java -XX:ArchiveClassesAtExit=application.jsa -Dspring.context.exit=onRefresh -jar application/my-app.jar

然后,你可以使用以下方式启动启用了 CDS 的应用程序:

java -XX:SharedArchiveFile=application.jsa -jar application/my-app.jar

Buildpacks 中的 CDS 和 Spring AOT 激活支持
自解压可执行 JAR 功能与 CDS 使用相结合,非常灵活,但仍然需要相当多的手动步骤,因此 Spring Boot 和 Buildpacks 为 CDS 提供了集成支持,其:

  • 创建容器镜像时自动执行训练运行。
  • 将 Spring Boot 可执行 JAR 提取到上面提到的 CDS 友好文件布局中。
  • 在集装箱内运送 CDS 档案。
  • 运行容器镜像时自动启用 CDS。

如https://github.com/sdeleuze/spring-boot-cds-demo存储库中所示,可以使用 Gradle 按照如下方式启用它:

tasks.named("bootBuildImage") {
    environment[
"BP_JVM_CDS_ENABLED"] = "true"
}

或者使用 Maven:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <image>
            <env>
                <BP_JVM_CDS_ENABLED>true</BP_JVM_CDS_ENABLED>
            </env>
        </image>
    </configuration>
</plugin>

在训练运行期间,Spring bean 会在没有启动 Spring 生命周期的情况下实例化,因此在实践中您可能会观察到的主要副作用是早期的数据库交互,这可以通过配置应用程序(或仅使用环境变量进行训练运行CDS_TRAINING_JAVA_TOOL_OPTIONS)来避免这种数据库交互,如此处所述

也可以使用BP_SPRING_AOT_ENABLED环境变量触发 Spring AOT 激活支持,但请务必牢记以下限制:

  • 在 Maven 或 Gradle 构建中启用 Spring AOT。
  • 可能配置 Spring AOT 以使用将在部署环境中使用的 Spring 配置文件。
  • CDS_TRAINING_JAVA_TOOL_OPTIONS且BP_SPRING_AOT_ENABLED无法合并。

性能提升结果
在 MacBook M2 上运行最小的 Spring MVC Tomcat 应用程序时,我们观察到,

  • 与运行可执行 JAR 相比,提取的应用程序与 CDS 相结合,启动速度提高了约 1.5 倍内存消耗降低了 16%
  • 如果我们将 Spring AOT 添加到组合中,启动速度将提高约 2 倍,内存消耗将降低 27%

Spring Boot 和 Leyden 项目
有趣的是,上面描述的新提取命令使用的 CDS 友好布局也旨在为Project Leyden Early-Access 版本提供最佳性能,该版本可看作是具有附加功能的 CDS 后继者,允许:

  • 启动速度更快。
  • 更小的容器镜像(通过删除 JDK 的 CDS 存档以仅保留应用程序镜像)。
  • 提前预热,以便启动后获得更好的性能并更快地达到峰值性能。

性能结果:
目前我们观察到:

  • 使用 Project Leyden 时 Spring Boot 应用程序的启动速度大约提高 3 倍
  • 而结合 Project Leyden 和 Spring AOT 时启动速度提高 4 倍

我将在 2024 年比利时 Devoxx 会议的 Project Leyden 演讲中分享更多信息,我很高兴与 Java 平台团队的 Per Minborg 共同演讲。