[Java] GraalVM 이란?

Oracle GraalVM은 고성능의 JIT(Just-In-Time) 컴파일러를 사용하여 Java 및 JVM 기반 애플리케이션의 성능을 가속화할 수 있는 고성능 JDK입니다. 이 솔루션은 애플리케이션 대기 시간을 줄이고 가비지 수집 시간을 단축하여 최대 처리량을 개선할 수 있습니다.

또한 AOT(Ahead-Of-Time) 컴파일러를 사용하여 미리 Java 바이트코드를 개별 운영체제 혹은 시스템에서 실행 가능한 이미지를 만드는 네이티브 이미지 유틸리티가 있습니다.

GraalVM 얼마나 좋은데?

GraalVM 설치

Installation on macOS

JAVA_HOME 설정

$ java --version
java 22.0.1 2024-04-16
Java(TM) SE Runtime Environment Oracle GraalVM 22.0.1+8.1 (build 22.0.1+8-jvmci-b01)
Java HotSpot(TM) 64-Bit Server VM Oracle GraalVM 22.0.1+8.1 (build 22.0.1+8-jvmci-b01, mixed mode, sharing

Spring Boot 벤치마크

GraalVM의 가장 독특한 기능인 AOT 컴파일러를 이용한 Native 이미지를 생성하여 기존 방식과의 차이점을 확인해보겠습니다.

일반적으로 많이 사용하는 Spring boot를 이용하여 실행 시간을 비교해보도록 하겠습니다.

비교 요소

  1. JDK 버전
  2. 컴파일 방식 (JIT, AOT)

Case1) JDK 11 + Boot 2.7.5

graalvm01

=> 8초

Case2) JDK 21 (temurin) + Boot 3.3.0

graalvm02

=> 2초

Case3) GraalVM 22 + AOT + Boot 3.3.0

graalvm03

=> 0.084초 !!

Case1에 비해 약 100배 정도 빠름.


Java 컴파일러 비교

Java는 C, C++ 등과 다르게 플랫폼에 대한 종속없이 실행될 수 있도록 JVM을 도입했습니다.

JVM의 사용은 플랫폼 종속성은 없앨 수 있었지만, 실행 시점에 JVM 위에서 바이트코드가 실시간으로 기계어로 번역(JIT Complie)하는 작업이 추가로 들어가야합니다.

이러한 이유로 실행 시, Java가 다른 언어보다 느리다고 알려져 있습니다.

JIT (Just-In-Time) 컴파일러

graalvm04

Java의 느린 단점을 보완하기 위해 HotSpot VM 기반의 고성능 JIT (Just-In-Time) 컴파일러가 Java 1.3 버전부터 추가되었습니다.

이 JIT 컴파일러의 목표는 빠른 컴파일 및 특정 환경에 맞춤화된 최적화입니다.

AOT (Ahead-Of-Time) 컴파일러

graalvm05

C, C++ 등에서 사용하는 컴파일러로 플랫폼에서 동작할 수 있는 네이티브 코드로 이루어진 실행 파일을 만들어냅니다.


Java의 AOT 컴파일러

graalvm05

사실 상 JIT 컴파일러는 애플리케이션 초기 웜업 시간이 필요하여 느리지만, 굉장히 오랜 연구 끝에 극도로 최적화가 잘되어 있어 실행 중인 시점에는 AOT 컴파일러를 능가하는 성능을 보여주기도 합니다.

그럼에도 Java에서 AOT 컴파일러를 적용하려는 이유는 아래와 같습니다.

  1. JIT 컴파일러는 C++ 로 되어 있으나, C++ 개발자를 구하기 힘듬.
  2. JIT 컴파일러는 오랜 기간 연구된 만큼 엄청 복잡 함.
  3. 최적화 측면에서 거의 한계에 봉착 함.

Native 이미지의 한계

graalvm06

위 그림을 보면 AOT와 JIT 컴파일러에 따른 초당 요청 처리 수를 보여주고 있습니다. AOT 컴파일러의 경우 Compile Time에 최적화를 진행하기 때문에 거의 일정한 성능을 보여줍니다, 반면 JIT의 경우 Run Time에 현재 실행 환경에 맞게 최적화를 진행합니다.

AOT 컴파일러가 초반의 빠른 실행과 처리에 초점이 맞춰져 있다면, JIT는 현재 실행환경에 맞게 최적화하는데 초점이 맞춰져 있다고 볼 수 있습니다.

또한 AOT 컴파일러를 통해 생성된 Native 이미지의 스타트가 빠른 이유는 바로 힙(Heap) 이미지 때문입니다. static 블록, 클래스 로딩 등을 미리 이미지화 시켜서 힙 이미지에 저장하여 빠른 실행이 가능합니다.

하지만 이렇게 정적으로 미리 이미지화 해두는 방식은 Java, Spring에서 사용하는 리플렉션, 클래스 로딩, 동적 프록시 등 다양한 동적 기술에 대해서 문제가 발생합니다.

JIT vs AOT

graalvm07

위 그림과 JIT 컴파일러와 AOT 컴파일러의 장단점을 구분할 수 있습니다.

따라서 적은 메모리 사용량, 작은 패키징, 빠른 실행이 필요한 경우 AOT를 레이턴시, 성능 등이 중요한 경우 JIT를 사용하는 식으로 시스템 요구사항에 맞게 선택하여 사용하면 좋을 것 같습니다.

Buy me a coffee
글이 도움이 되셨다면, 커피 한 잔만 사주세요!
Comments
Copied to clipboard