Bigdata, Scala / GraalVM

Jeonghak Cho·2025년 3월 20일

Bigdata

목록 보기
6/30

GraalVM 소개

GraalVM은 Oracle이 개발한 고성능 JVM 및 네이티브 실행 환경으로, 기존 JVM 대비 더 빠른 실행 속도, 낮은 메모리 사용량, 다중 언어 지원을 제공하는 게 특징이다. 특히, Scala(SBT)와 Rust를 함께 사용하려면 GraalVM을 활용하여 빌드 속도를 크게 개선할 수 있다.

주요 특징

  • Just-In-Time (JIT) 컴파일 최적화: GraalVM의 고급 JIT 컴파일러는 기존 HotSpot JVM보다 더 빠른 실행 속도를 제공
  • Native Image (AOT 컴파일): Scala, Java 코드를 네이티브 바이너리(.exe, .out)로 변환해서 실행 속도를 극대화
  • JVM 없이 실행 가능 (메모리 사용량 50% 절감): Rust처럼 네이티브 바이너리 실행 가능, 서버리스 환경 (AWS Lambda, Kubernetes)에서 빠른 시작 시간 제공
  • 다중 언어 지원: GraalVM은 Rust, Python, JavaScript, R, Ruby, Wasm 등 다양한 언어를 실행한다. Rust와 Scala를 함께 사용하는 경우, 같은 환경에서 실행 가능하고, Polyglot API로 Scala에서 Rust 코드를 호출 가능하다.

GraalVM 설치

GraalVM 버전과 JDK 호환성

GraalVM 버전호환되는 JDK 버전
GraalVM 11JDK 11 (LTS)
GraalVM 17JDK 17 (LTS)
GraalVM 21JDK 21

선택 가이드

  • GraalVM 11 → JDK 11 기반 프로젝트에서 사용
  • GraalVM 17 → JDK 17 LTS 기반 프로젝트에서 사용 (안정적, 장기 지원)
  • GraalVM 21 → JDK 21 기반 최신 기능 활용 시 선택

OpenJDK 17 설치

wget https://download.java.net/openjdk/jdk17/ri/openjdk-17+35_linux-x64_bin.tar.gz
tar -xvzf openjdk-17+35_linux-x64_bin.tar.gz
sudo mv jdk-17 /opt/openjdk17

echo 'export JAVA_HOME=/opt/openjdk17' >> ~/.bashrc
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> ~/.bashrc
source ~/.bashrc

vagrant@slave1:~$ java --version
openjdk 17 2021-09-14
OpenJDK Runtime Environment (build 17+35-2724)
OpenJDK 64-Bit Server VM (build 17+35-2724, mixed mode, sharing)

GraalVM 21 버전에서는 gu(GraalVM Updater) 도구가 기본적으로 포함되지 않으며, native-image도 자동으로 설치되지 않는다.
이러한 경우, native-image를 수동으로 설치해야 한다. 그러나, GraalVM 21 버전은 현재 사용 중인 운영 체제와의 호환성 문제로 인해 일부 기능이 제한될 수 있다.
따라서, 안정적인 설치 및 사용을 위해 GraalVM 17 버전으로 권장한다. GraalVM 17은 LTS(Long-Term Support) 버전으로, gu 도구와 native-image가 기본적으로 포함되어 있어 설치 및 사용이 용이하다.

wget https://github.com/graalvm/graalvm-ce-builds/releases/download/jdk-17.0.9/graalvm-community-jdk-17.0.9_linux-x64_bin.tar.gz

tar -xvzf graalvm-community-jdk-17.0.9_linux-x64_bin.tar.gz
sudo mv graalvm-community-openjdk-17.0.9+9.1 /opt/graalvm

export PATH="/opt/graalvm/bin:$PATH"
export JAVA_HOME="/opt/graalvm"

echo 'export PATH="/opt/graalvm/bin:$PATH"' >> ~/.bashrc
echo 'export JAVA_HOME="/opt/graalvm"' >> ~/.bashrc
source ~/.bashrc

버전 확인 시 아래 처럼 GraalVM CE 17.0.9+9.1 문구가 올라와야 한다.

vagrant@slave1:~$ java --version
openjdk 17.0.9 2023-10-17
OpenJDK Runtime Environment GraalVM CE 17.0.9+9.1 (build 17.0.9+9-jvmci-23.0-b22)
OpenJDK 64-Bit Server VM GraalVM CE 17.0.9+9.1 (build 17.0.9+9-jvmci-23.0-b22, mixed mode, sharing)

"Native Image" 기능을 사용하면 JVM 없이 실행 파일을 생성 가능하다.
하지만 아래 사항의 문제점이 있으니 참고하자.

JVM 기반 대규모 엔터프라이즈 애플리케이션에서 활용이 힘들 수 있다.

  • 기존 JVM 최적화(JIT) 를 활용하는 앱에서는 오히려 성능 저하 가능
  • AOT 컴파일의 한계로 JVM 동적 로딩, Reflection 지원이 제한적 - 기존 Java 애플리케이션 이식
  • native-image 빌드 과정이 복잡하고 오류가 많음
  • 특히 Scala 환경에서는 이슈가 많음 - 빌드 시간 & 디버깅 어려움
  • native-image 빌드 시간이 길고, 디버깅이 어렵다

벤치마킹용 소스

public class Benchmark {
    public static void main(String[] args) {
        long start = System.nanoTime();
        int count = countPrimes(50_000);
        long end = System.nanoTime();

        System.out.println("Prime Count: " + count);
        System.out.println("Execution Time: " + (end - start) / 1e6 + " ms");
    }

    private static int countPrimes(int limit) {
        int count = 0;
        for (int i = 2; i <= limit; i++) {
            if (isPrime(i)) {
                count++;
            }
        }
        return count;
    }

    private static boolean isPrime(int num) {
        if (num < 2) return false;
        for (int i = 2; i * i <= num; i++) {
            if (num % i == 0) return false;
        }
        return true;
    }
}

Native Image 생성

sudo apt update
sudo apt install build-essential
gcc --version

sudo apt update
sudo apt install zlib1g-dev

Native Image만드는 시간이 많이 든다.

vagrant@slave1:~$ native-image Benchmark
========================================================================================================================
GraalVM Native Image: Generating 'benchmark' (executable)...
========================================================================================================================
Warning: The host machine does not support all features of 'x86-64-v3'. Falling back to '-march=compatibility' for best compatibility.
[1/8] Initializing...                                                                                    (5.6s @ 0.08GB)
 Java version: 17.0.9+9, vendor version: GraalVM CE 17.0.9+9.1
 Graal compiler: optimization level: 2, target machine: compatibility
 C compiler: gcc (linux, x86_64, 9.4.0)
 Garbage collector: Serial GC (max heap size: 80% of RAM)
[2/8] Performing analysis...  [****]                                                                    (21.5s @ 0.30GB)
   2,908 (71.64%) of  4,059 types reachable
   3,529 (51.03%) of  6,915 fields reachable
  13,179 (43.69%) of 30,164 methods reachable
     906 types,     0 fields, and   348 methods registered for reflection
      58 types,    58 fields, and    52 methods registered for JNI access
       4 native libraries: dl, pthread, rt, z
[3/8] Building universe...                                                                               (2.9s @ 0.34GB)
[4/8] Parsing methods...      [**]                                                                       (2.5s @ 0.29GB)
[5/8] Inlining methods...     [***]                                                                      (1.4s @ 0.29GB)
[6/8] Compiling methods...    [****]                                                                    (19.2s @ 0.30GB)
[7/8] Layouting methods...    [*]                                                                        (1.0s @ 0.45GB)
[8/8] Creating image...       [[8/8] Creating image...       [*]
         (1.5s @ 0.31GB)
   4.38MB (36.55%) for code area:     7,486 compilation units
   7.03MB (58.63%) for image heap:   89,135 objects and 5 resources
 592.55kB ( 4.83%) for other data
  11.99MB in total
------------------------------------------------------------------------------------------------------------------------
Top 10 origins of code area:                                Top 10 object types in image heap:
   3.35MB java.base                                         1006.82kB byte[] for code metadata
 770.68kB svm.jar (Native Image)                             888.03kB java.lang.String
 111.98kB java.logging                                       835.41kB byte[] for general heap data
  61.92kB org.graalvm.nativeimage.base                       671.02kB java.lang.Class
  23.82kB jdk.internal.vm.ci                                 664.54kB byte[] for java.lang.String
  23.10kB org.graalvm.sdk                                    347.30kB java.util.HashMap$Node
   6.10kB jdk.internal.vm.compiler                           249.91kB com.oracle.svm.core.hub.DynamicHubCompanion
   1.94kB Benchmark                                          168.67kB java.lang.String[]
   1.35kB jdk.proxy1                                         165.57kB java.lang.Object[]
   1.27kB jdk.proxy3                                         148.84kB byte[] for embedded resources
   1.56kB for 2 more packages                                  1.19MB for 825 more object types
Warning: The host machine does not support all features of 'x86-64-v3'. Falling back to '-march=compatibility' for best compatibility.
------------------------------------------------------------------------------------------------------------------------
Recommendations:
 HEAP: Set max heap for improved and more predictable memory usage.
 CPU:  Enable more CPU features with '-march=native' for improved performance.
------------------------------------------------------------------------------------------------------------------------
                        4.3s (7.5% of total time) in 90 GCs | Peak RSS: 1.05GB | CPU load: 1.89
------------------------------------------------------------------------------------------------------------------------
Produced artifacts:
 /home/vagrant/benchmark (executable)
========================================================================================================================
Finished generating 'benchmark' in 56.3s.

java 수행과 native image 수행 속도 비교

java로 실행 - 3.6 초

vagrant@slave1:~$ time java Benchmark
Prime Count: 5133
Execution Time: 3.674163 ms

real    0m0.097s
user    0m0.032s
sys     0m0.111s

native image로 실행 - 1.5초

vagrant@slave1:~$ time ./benchmark
Prime Count: 5133
Execution Time: 1.502573 ms

real    0m0.006s
user    0m0.007s
sys     0m0.000s

0개의 댓글