Kakaopay 기술블로그: Google Cloud Serverless for Java developer 글을 읽고,
JVM Cold Start 완화 방법에 대해 정리한 내용입니다.
원본 글에서 아래의 내용을 추가하였습니다.
Started <ApplicationName> in X seconds (process running for Y)
프로젝트에서 큰 변화 없이 단순히 최신 JDK 버전으로 사용하기만 해도 성능 향상이 있습니다.
JVM은 시작하는 단계에서 동적 로딩, 지연 초기화 등으로 인해 많은 연산이 수행됩니다.
이 과정에서 GC도 종종 발생하기 때문에, 환경에 따른 적절한 GC를 선택하는 것도 방법일 수 있습니다.
JDK 17과 21에서는 G1 GC가 기본으로 적용됩니다. JDK 17부터 출시된 ZGC의 경우는 대용량을 처리해야 하는 상황일 경우에 성능이 좋다고 합니다. 자세한 기준은 아래 표를 참고하시면 됩니다.
java -jar -XX:+Use{사용할 GC 이름} {프로젝트이름}.jar
위와 같이 java -jar
명령어로 실행시, 각 GC별로 아래의 옵션을 추가하시면 됩니다.
-XX:+UseG1GC
-XX:+UseZGC
-XX:+UseParallelGC
-XX:+UseSerialGC
-XX:+UseShenandoahGC
JVM은 JIT(Just In Time) 컴파일러를 이용해 컴파일 수준(Level)을 나누고, 각 수준에 맞춰 다른 컴파일러가 동작합니다. 이때, 각각의 컴파일러는 다른 목표를 가지고 있습니다.
다만, 단순한 서비스나 C1 컴파일로 성능을 만족하는 경우에는 C2 컴파일러를 사용할 필요가 없습니다.
따라서 아래의 옵션을 사용하여 C1 컴파일러만 동작하도록하여 Cold Start를 완화시킬 수 있습니다.
java -jar -XX:TieredStopAtLevel=1 {프로젝트이름}.jar
TieredStopAtLevel 레벨 설명
- 0 : Interpreted Code
- 1: Interpreted Code
- 2 : Limited C1 Compiled Code
- 3 : Full C1 Compiled Code (기본값)
- 4 : C2 Compiled Code
CDS(Class Data Sharing)는 클래스를 로딩하는 과정에서 로컬 공유 아카이브에 저장된 CDS 파일(.jsa)을 참조하여 기동 하는 방식입니다.
자주 사용하는 클래스의 메타데이터(클래스 구조, 메소드, 필드 등)들이 CDS 파일을 저장하면 시작 시간을 효과적으로 줄일 수 있습니다.
아래 명령어는 기본 위치인 JAVA_HOME/lib/server/classes.jsa
에 기본 아카이브를 생성합니다.
java -Xshare:dump
아래 명령어는 해당 명령어를 실행한 위치에 MyApp.jsa
아카이브를 생성합니다.
java -XX:ArchiveClassesAtExit=MyApp.jsa -cp MyApp.jar MyMainClass
실행 시간 : 2.248초
java -XX:SharedArchiveFile=MyApp.jsa -cp MyApp.jar MyMainClass
실행 시간 : 2.107초
CRaC(Coordinated Restore at Checkpoint)는 기동 중인 애플리케이션을 중단한 채로 저장해두고,
트래픽이 유입되어 추가 애플리케이션이 필요할 때 복원하는 방식입니다.
이를 위해서 체크포인트라고 하는 인스턴스의 현재 상태를 파일로 저장합니다. 가장 큰 장점은 하나의 체크포인트로 여러 개의 인스턴스를 복원할 수 있다는 점으로, 트래픽 폭증 시 빠른 확장에 적절할 것 같습니다.
오라클은 앞에서 말한 C2 컴파일러를 대체하는 Graal 컴파일러를 만들었습니다.
Leyden은 Serverless, Cloud 환경에서 애플리케이션 최적화를 위해 시작된 OpenJDK 프로젝트입니다.
성능을 최적화하기 위해 CDS, Graal 컴파일러를 활용한 AOT Compilation, GraalVM의 SubstrateVM 기술을 포함한 모든 Java 리소스를 검토하고 있다고 합니다.
Framework에는 Startup 최적화를 위해 GraalVM의 SubstrateVM 기술을 활용하여, 애플리케이션 코드를 네이티브 바이너리로 변환하는 Spring AOT가 존재합니다.
Premain 최적화를 적용하여 시작 시간을 기존보다 2~4배 단축할 수 있는 것을 확인했다는 자료도 있습니다.
첫번째로 나온 최신 JDK 사용 및 GC 튜닝하는 방법을 제외하고는 전부 처음보는 방법이였어서 새로운 접근 방식으로 다가와 신선했습니다.
CRaC와 GraalVM의 경우, 쿠버네티스와 같은 컨테이너 기술에 대한 지식이 부족하여 아쉽게도 직접 비교를 해보지는 못했습니다.
그래도 앞으로 계속 새로운 최적화 방법들이 나올 예정이니 계속해서 관심을 가져야겠습니다.
어떤 인생을 살고있는거냐 동엽아...
화이팅이다!!