Spring Boot 3用CDS提升20%启动时间

2024 年 5 月 23 日,Spring Boot 团队发布了3.3.0版本:“CDS 支持改善启动时间并减少内存消耗。”

在本文中,我们将创建一个名为GreetingsApp的简单 Java Web Spring Boot应用程序。然后,我们将比较标准 Uber JAR 和在 jarmode 的帮助下提取 Uber JAR 创建的 CDS 友好布局中的 JAR 的启动时间和内存占用。

什么是类数据共享 (CDS)
类数据共享 (CDS)是 Java 虚拟机 (JVM) 中的一项功能,可帮助缩短启动时间并减少 Java 应用程序的内存占用。

CDS 的工作原理是:

  • 创建一个包含标准 Java 库中的类的共享存档文件。
  • 当应用程序启动时,JVM 可以从共享存档中加载这些类,而不是单独加载它们。
这不仅可以加快启动过程,还可以允许多个 JVM 实例在内存中共享相同的类数据,从而减少总体内存使用量。

先决条件
如果您想继续,您的机器上必须安装Java 17+和Docker 。

创建 Spring Boot 应用程序
让我们使用Spring Initializr创建一个 Spring Boot 应用程序。
应用程序名称将是GreetingsApp,唯一的依赖项是Spring Web。

我们将使用 Spring Boot 版本3.3.0和 Java 17。

创建 GreetingsController 类

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/greetings")
public class GreetingsController {
    
    @GetMapping
    public String greeting(@RequestParam(required = false) String name) {
        return
"Hello %s!".formatted(name == null ? "World" : name);
    }
}


这个 Java 类处理 GET 请求,/greetings如果提供了参数则返回“Hello [name]!” name,如果没有提供则返回“Hello World!”。

创建 MemoryUsageLogger 类
在com.example.GreetingsApp包中,让我们创建MemoryUsageLogger具有以下内容的类:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class MemoryUsageLogger implements ApplicationRunner {

    private static final Logger logger = LoggerFactory.getLogger(MemoryUsageLogger.class);

    @Override
    public void run(ApplicationArguments args) {
        Runtime runtime = Runtime.getRuntime();
        long memoryUsedBytes = runtime.totalMemory() - runtime.freeMemory();
        long memoryUsedMB = memoryUsedBytes / (1024 * 1024);
        logger.info("Memory footprint at startup: {} MB", memoryUsedMB);
    }
}

此 Java 类记录启动时的内存使用情况。当应用程序启动时,它会计算已用内存并使用 SLF4J 记录。

生成 Uber JAR
在终端和 GreetingsApp 根文件夹中,运行以下命令来打包 Uber JAR:

./mvnw clean package -DskipTests

该GreetingsApp-0.0.1-SNAPSHOT.jar文件将在目标中生成folder。

生成 CDS 友好布局
在终端和 GreetingsApp 根文件夹中,运行以下命令来生成 CDS 友好布局:

java -Djarmode=tools -jar target/GreetingsApp-0.0.1-SNAPSHOT.jar extract

将会创建一个名为的文件夹GreetingsApp-0.0.1-SNAPSHOT。其中,我们将包含GreetingsApp-0.0.1-SNAPSHOT.jar文件(我们将其称为CDS JAR)和lib包含所有所需库的文件夹。

启动 5 次 Uber JAR
在终端的 GreetingsApp 根文件夹中,运行以下命令来启动该应用程序。按Ctrl+C关闭它,并重复此过程五次:
java -jar target/GreetingsApp-0.0.1-SNAPSHOT.jar

以下是我们进行的 5 次执行的结果(为简洁起见,省略了一些日志行):


:: Spring Boot :: (v3.3.0) 
... 
2024-05-26T10:37:22.651+02:00 INFO 99104 --- [GreetingsApp] [ main] ceGGreetingsAppApplication :在 2.401 秒内启动 GreetingsAppApplication(进程运行 2.984 秒)
2024-05-26T10:37:22.655+02:00 INFO 99104 --- [GreetingsApp] [ main] ceGreetingsApp.MemoryUsageLogger :启动时的内存占用:14 MB
 ^C% 


在 CDS 友好布局中启动 5x JAR
在终端的 GreetingsApp 根文件夹中,运行以下命令来启动该应用程序。按Ctrl+C关闭它,并重复此过程五次:

java -jar GreetingsApp-0.0.1-SNAPSHOT/GreetingsApp-0.0.1-SNAPSHOT.jar

以下是我们进行的 5 次执行的结果(为简洁起见,省略了一些日志行):


:: Spring Boot :: (v3.3.0) 
... 
2024-05-26T10:41:57.459+02:00 INFO 99303 --- [GreetingsApp] [ main] ceGGreetingsAppApplication : 在 1.947 秒内启动 GreetingsAppApplication(进程运行了 2.275 秒)
2024-05-26T10:41:57.462+02:00 INFO 99303 --- [GreetingsApp] [ main] ceGreetingsApp.MemoryUsageLogger : 启动时的内存占用:18 MB
 ^C% 


比较:Uber JAR 与 CDS JAR
下表显示了上述执行获得的启动时间和内存占用:
JAR 类型|启动时间(秒) |启动时的内存占用(MB) | 

-------- + ------------------- + -------------------------------- | 
Uber JAR | 2.369 | 19 | 
Uber JAR | 2.389 | 19 | 
Uber JAR | 2.359 | 33 | 
Uber JAR | 2.401 | 14 | 
Uber JAR | 2.425 | 19 | 
........ + ................... + ................................ | 
CDS JAR | 1.947 | 18 | 
CDS JAR | 2.032 | 14 | 
CDS JAR | 1.953 | 16 | 
CDS JAR | 1.908 | 17 | 
CDS JAR | 1.940 | 18 |

平均值为:
JAR 类型|平均启动时间(秒) |启动时平均内存占用(MB) | 

-------- + ----------------------- + ------------------------------------ | 
Uber JAR | 2.388 | 20.8 | 
........ + ......................... + .................................... | 
CDS JAR | 1.956 | 16.6 |

从平均值表中我们可以看出:

  • CDS JAR 的启动时间比 Uber JAR 快约18.09%
  • 此外,内存占用也减少了约20.19%。

结论
在本文中,我们创建了一个名为 GreetingsApp 的简单 Java Web Spring Boot 应用程序,以探索类数据共享 (CDS) 的好处。通过比较标准 Uber JAR 与 CDS JAR 的启动时间和内存占用,我们展示了 CDS 如何增强 Java 应用程序。结果表明,使用 CDS 可显著缩短启动时间和减少内存占用,缩短约 20%,使其成为优化 Java 应用程序的宝贵功能。

Java 有一些改进,特别是GraalVM和CRaC。在 CDS、GraalVM 和 CRaC 之间做出选择时,重要的是要考虑它们的准备情况和易用性。虽然 GraalVM 和本机构建很有前景,但它们可能尚未完全准备好用于生产。另一方面,CDS 已经成熟且易于实现,使其成为优化 Java 应用程序的启动时间和内存使用情况的可靠选择。