https://spring.io/blog/2023/10/16/runtime-efficiency-with-spring에 대한 후속 조치로 Project Leyden 최적화를 위해 많이 사용되지 않는 JDK의 CDS(lass Data Sharing) 기능에 대한 지원이 Spring Framework 6.1에 제공되어 CDS에 대하여 알아본다.
GraalVM, Proejct CRaC를 사용하면 Spring Boot 애플리케이션을 수십 밀리초에 시작할 수 있다. 하지만 왜 CDs에 관심을 가져야 하는지 알아보자.
- GraalVM 및 Proejct CRaC보다 제약 조건과 부작용이 적고, OpenJDK에서 즉시 사용 가능한 기술이다.
- 대부분의 사람들은 CDS를 사용하지 않지만 상대적으로 적은 작업으로 합리적인 시작 시간의 개선할 수 있다.
- 새로운 JVM 릴리즈에서 점점 개선되고 있으며, Project Leyden은 가까운 시일 내에 더 많은 기능이 추가될 것이다.
CDS
CDS(lass Data Sharing) 는 Java 애클리케이션 시작 시간과 메모리 공간을 중이는 데 도움이 되는 JVM 기능이다.
이 기능을 사용하려면 애플리케이션의 특정 클래스 경로에 대해 CDS 아카이브를 생성해야 한다. Spring Framework는 아카이브 생성을 쉽게 하기 위한 hook-point를 제공한다. 아카이브를 사용할 수 있으면, 사용자는 JVM 플래그를 통해 CDS를 사용할 수 있다.
Creating the CDS Archive
애플리케이션이 종료되면 애플리케이션에 대한 CDS 아카이브를 생성할 수 있다. Spring Framework는 ApplicationContext가 새로 고쳐지면 자동으로 종료될 수 있는 작업 모드를 제공한다. 이 모드는 non-lazy로 초기화된 싱글톤이 인스턴스화 되고 InitializingBean#afterPropertiesSet 콜백이 호출된다. 그러나 수명 주기가 시작되지 않았으며 ContextRefreshedEvent가 아직 게시되지 않는다.
아카이브를 생성하려면 두 개의 추가 JVM 플래그를 지정해야 한다.
- -x:ArchiveclassesAtExit=application.jsa: 종료 시 CDS 아카이브를 생성한다.
- -Dspring. context. exit=onRefresh: 위 설명대로 Spring 애플리케이션을 시작한 다음 즉시 종료한다.
CDS아카이브를 생성하려면 JDK/JRE에 기본 이미지가 있어야 합니다. 위의 플래그를 시작 스크립트에 추가 면 다음과 같은 경고가 표시될 수 있다.
-XX:ArchiveClassesAtExit is unsupported when base CDS archive is not loaded. Run with -Xlog:cds for more info.
CDS 아카이브는 일반적으로 기본적으로 제공되지만 필요한 경우 다음 명령을 실행하여 생성할 수도 있다.
§ java -Xshare: dump
Using the Archive
아카이브 사용이 가능하면, application.jsa 파일이 작업 디렉토리에 있다고 가정하면, 시작 스크립트에 xx:SharedArchiveFile=application.jsa 를 추가하여 사용한다.
CDS 캐시가 효율적인지 확인하려면, -xshare:on을 사용하면 CDS를 사용할 수 없으면 에러 메시지를 프린트 하고 종료한다. (테스트 용도로만 사용해야한다! not in production)
캐시가 얼마나 효과적인지 파악하려면 -xlog:classt+load:file=cds.log를 추가하면 클래스 로딩 로그를 활성화할 수 있다. cds.log가 생성되면 클래스 로드를 시도할때마다 로그가 생성된다. 캐시에서 로드되는 클래 스에는 아래 예제와 같이 "shared objecs file” 소스가 있어야한다.
[0.064s][info][class,load] org.springframework.core.env.EnvironmentCapable source: shared objects file (top)
[0.064s][info][class,load] org.springframework.beans.factory.BeanFactory source: shared objects file (top)
[0.064s][info][class,load] org.springframework.beans.factory.ListableBeanFactory source: shared objects file (top)
[0.064s][info][class,load] org.springframework.beans.factory.HierarchicalBeanFactory source: shared objects file (top)
[0.065s][info][class,load] org.springframework.context.MessageSource source: shared objects file (top)
CDS를 활성화할 수 없거나 캐시에서 로드되지 않는 클래스가 많은 경우 아카이브를 생서어하고 사용할 때 다 음 조건이 충족되는지 확인이 필요하다.
- 동일한 JVM을 사용해야한다.
- 클래스 경로는 JAR 목록을 지정되어야 하며 디렉터리 및 * 와일드카드 문자는 사용할 수 없다.
- JAR의 타임스탬프를 보존해야한다.
- 아카이브를 사용할 때 클래스 경로는 아카이브를 생성하는 데 사용된 클래스 경로와 동일해야 하며 동일 한 순서로 이루어져야한다. 추가 JAR 또는 디렉터리는 끝에 지정할 수 있다. (캐시되지 않음)
Example
Spring Boot 프로젝트에 gadle 플러그인으로 aot를 추가해준다.
테스트 장비 : 4 Core, 8GB Ram, JDK 21
build.gradle
plugins {
id 'java'
id 'org.springframework.book' version '3.3.0'
id 'org.springframework.book.aot' version '3.3.0' // 추가
id 'org.springframework.book' version '3.3.0'
}
...
bootRun
실행 시간 : 약 1.9초
$ ./gradlew bootRun
> Task :processAot
......
... Started SpringBootCDSApplication in 1.679 seconds (process running for 1.904)
Build and run with CDS
Build
§ ./gradlew build
BUILD SUCCESSFUL in 13s
# 참고 https://github.com/sdeleuze/spring-cds-demo/blob/main/unpack-executable-jar.sh
$ ./unpack-executable-jar.sh -d build/unpacked build/libs/spring-boot-cds-0.0.1-SNAPSHOT
Application successfully extracted to 'build/unpacked'
Perform the CDS training run
$ java -Dspring.aot.enabled=true - Dspring.context.exit=onRefresh -XX:ArchiveClassesAtExit=build/unpacked/application.jsa -jar build/unpacked/run-app.jar
# Spring 실행 후 자동 종료
Run the application with CDS optimizations
1.2초로 실행 시간이 30~40% 감소하였다.
$ java -Dspring.aot.enabled=true -XX:SharedArchiveFile=build/unpacked/application.jsa -jar build/unpacked/run-app.jar
......
... Started SpringBootCDSApplication in 1.129 seconds (process running for 1.296)
참고
'dev > spring' 카테고리의 다른 글
[Spring Framework 6.2] Bean Background Initialization (0) | 2024.06.13 |
---|---|
Hello, Java 22! (0) | 2024.06.09 |
REST Assured로 API 테스트하기 (0) | 2024.06.03 |
Spring - Modulith (0) | 2024.06.03 |
Spring Framework - Runtime efficiency with String (today and tomorrow) (0) | 2024.06.02 |