GraalVM

블러거·2026년 2월 6일

JVM

목록 보기
23/26

GraalVM

jaotc

Jaotc 는 JDK 9 ~ JDK 16 에 포함된 JDK AOT 컴파일러이다.
JEP 295 에서 제안되었으며 JEP 410 에서 사라졌다.

jaotc --output jaotcResult.so jaotcSource.class

와 같이 미리 AOT 컴파일러로 so 파일을 만들고,

java -XX:+UnlockExperimentalVMOptions -XX:AOTLibrary=./jaotcResult.so HelloWorld.class

와 같이 실행할 수 있다.

하지만 JEP 295 에도 적혀 있듯, 아무도 쓰지도 않고 사용 자체도 어려워서 JDK 에서 빠지게 되었다. 그래서 굳이 jaotc 를 공부할 필요는 없다.

대신 jaotc 는 내부적으로 Graal Compiler 를 사용했으며, jaotc 는 JDK 16 에서 빠지고 난 후 Graal 프로젝트로 편입되었다.

따라서 이제는 GraalVM 을 알아봐야 한다.

GraalVM

Graal VM 은 다음과 같은 기능들을 내걸고 있다.

  • Graal Compiler 를 사용하여 바로 실행 가능한 Native-Image 생성(AOT)
  • 일반 JVM 의 C2 대신 Graal Compiler 사용 가능
  • 고성능 및 메모리 사용량 감소 컴파일러
  • Polyglot(ex. 자바에서 JS 코드 사용 가능)

이러한 기능들이 가능한 근본은 Graal Compiler 때문이다.

GraalVM

GraalVM 은 HotSpot 을 기반으로 만들어진 일종의 JVM 이다.
C2 컴파일러 대신 Graal Compiler 가 동작한다.

GraalVM 은 HotSpot 처럼 일반적인 java 파일을 실행할 수 있으며,
Native Image 생성시에도 사용될 수 있다.

Graal Compiler

그랄 컴파일러는 처음에는 HotSpot 의 C2 를 대체하기 위한 컴파일러였다.
여기에 다른 몇가지 요구사항들이 합쳐지면서 위의 기능을 모두 제공해주는 GraalVM 의 근본이 되었다.

몇가지 중요한 특징을 알아보자

1. 자바로 작성된 컴파일러다

C++ 로 짜여진 C2 컴파일러에 반해, 그랄 컴파일러는 JEP-243 의 JVMCI 덕분에 자바로 작성될 수 있었다.

  • 자바 컴파일러 개발자들이 익숙한 언어로 개발을 더 쉽게 할 수 있다.
  • 일반 개발자들이 컴파일러를 디버깅할 수 있다. (중단점 걸고 볼 수 있음)

2. 언어 독립적인 컴파일러다.

언어 독립성이란 말 그대로다. JS 를 GraalVM 에서 실행시킬 수 있다는 것이다.
하지만 중요한 것은 JS 의 특성 및 최적화는 적용되어야 한다.

Graal Compiler 개발진은 애초부터 여러 언어를 지원할 수 있는 컴파일러를 만들고 싶어 했다.

한편 생각해보면 JS 를 Java Class File 로 변환하는 컴파일러를 만들면 되지 않는가? 모든 언어를 Java Class File 로 만드는 컴파일러를 만들면 이미 C2 는 언어 독립적인 컴파일러 같기도 하다.

하지만 그렇지 않다. Java Class File 은 이미 스택 구조, 메서드 호출 규약(invokevirtual) 등 이미 자바에 맞춰진 파일이다.
JS 를 Java Class File 로 바꾼다면 JS 의 동적인 특성은 모두 사라지고 없을 것이다.

그렇다면 어떻게 모든 언어를 대상으로한 독립성을 지킬 수 있을까.
모든 언어는 다르게 생겼지만 하고자 하는 것은 같다. 하드웨어를 동작시켜서 원하는 동작을 하기 위해 언어를 작성하는 것이다. 즉, 실행 흐름 은 모든 언어에서 같을 것이다.

Graal Compiler 는 실행 흐름을 컴파일한다.
실행 흐름을 나타내는 Graph 라는 Graal IR 을 네이티브 코드로 변환한다. 이 방식으로 언어의 독립성을 실현했다.

비교하자면
C2 Compiler 는 Java Class File 을 네이티브 코드로 컴파일하지만,
Graal Compiler 는 Graph(Graal IR) 을 네이티브 코드로 컴파일한다.

3. native-image 생성에 사용된다.

4. 3번과 더불어, HotSpot 내부의 C2 대체 JIT 컴파일러로도 쓰일 수 있다.

  • GraalVM 은 C2 대신에 그랄 컴파일러가 내장된 JVM 이다. 우리가 작성하던 왠만한 코드는 모두 실행할 수 있다.
  • JDK 25 에서는 제거되었지만, 예를 들어 JDK 24 에서는 java 옵션으로도 C2 컴파일러 대신 Graal Compiler 를 사용할 수 있다.

GraalVM 설치

가장 먼저 GraalVM 을 설치해야 한다.

Mac 에서 가장 쉽게 GraalVM 을 사용하는 방법은 SDKMAN 을 사용하는 것이다.

https://www.graalvm.org/latest/getting-started/macos/#sdkman

Native Image 만들기

GraalVM 은 여러가지 방법으로 Native Image 를 만들 수 있다.

클래스, jar, java-module 을 gradle, maven, cli tool 을 사용하여 네이티브 이미지를 만들 수 있다.

이번에는 간단하게 native-image cli tool 을 써볼 예정이다.
native-image cli tool 은 GraalVM 내부에서 제공하는 툴로써, GraalVM 과 내부의 Graal Compiler 를 사용하여 자바 코드를 Native Image 파일로 생성할 수 있다.

Polyglot 을 이용하면 타 언어도 가능

native image 에는 사전 컴파일된(AOT) 네이티브 코드와 해당 네이티브 코드를 JVM 없이 실행할 수 있는 런타임(Substrate VM) 이 포함되어 있다.

Native Image 특징

  • 독립 실행 가능하다(SubStrate VM)
  • 모든 코드를 분석하여 가상 메서드를 직접 호출 메서드로 만든다
    • 가상 메서드는 런타임에 리시버에 따라 호출할 메서드를 동적으로 디스패치 해야 하기 때문에 JIT 컴파일에서 섣불리 최적화(ex. inline)하지 못한다. 모든 코드를 분석하려면 시간이 많이 걸리고, 런타임에 이를 수행하기 부담스럽기 때문이다. 따라서 예측 기반 공격적 최적화를 할 수 밖에 없다.
    • 반면 AOT 방식인 Graal Native-Image 에서는 사전에 모든 코드를 분석하여 가상 메서드를 직접 호출 메서드로 바꾼다. 이는 JIT 는 할 수 없는 추가 최적화가 가능하도록 만든다.
  • 런타임에 동작하는 몇몇 기능을 사용하지 못한다.
    • 리플랙션, Dynamic Proxy, Dynamic Class Loading, Runtime Annotation Processor 를 사용하지 못한다.
    • 따라서 Dynamic Class Loading 을 사용하는 Logback, 리플랙션을 사용하는 Jackson 등을 사용하지 못할 수 있다.
    • GraalVM 이 제공하는 Trace Agent 를 사용하여 해결해야 한다.

Native Image 실습

https://www.graalvm.org/latest/reference-manual/native-image/#from-a-class

  1. java 파일 생성
public class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello, World!");
  }
}
  1. 컴파일 및 네이티브 이미지 생성
 javac HelloWorld.java
 native-image HelloWorld

  1. 실행
./helloworld

Polyglot


https://giljae.medium.com/graalvm-%EC%86%8C%EA%B0%9C-84ac547f8df2
GraalVM 을 이용하면 JS 코드를 Java 에서 사용할 수 있다.
또는 JS 파일에 작성된 함수를 Java 에서 불러와 실행할 수 있다.
JS 뿐만 아니라 C, C++, Ruby, Python 등등 많은 언어가 가능하다.

이를 Polyglot 이라 한다.

Truffle 은 언어를 만드는 프레임워크다.
언어를 만든다는건 사실 언어를 기계어로 바꾸는 인터프리터를 만드는 것이다.

Truffle 프레임워크로 만든 애플리케이션은 JS, Python, R 또는 커스텀한 언어를 AST 로 만드는 AST 인터프리터다. AST 는 원래 언어(JS, Python ..) 의 실행 흐름을 담고 있는 자바 객체 트리로서, JVM 에서 실행하면 원래 언어의 코드가 실행되는 것이다.

JS는 HotSpot JVM에서 실행된다.
다만 HotSpot이 JS를 직접 이해해서 실행하는 게 아니라,
Truffle로 구현된 JS 인터프리터(자바 코드)를 실행함으로써 실행된다.

Truffle 프레임워크로 만들어진 인터프리터가 변환한 AST 는 자바 객체이기 때문에 GraalVM 이 아니더라도 HotSpot 에서도 실행될 수 있다.

하지만 Graal Compiler 를 만났을 때 가장 높은 수준의 최적화가 이뤄질 수 있다. 다음 표를 보자.


https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support

Polyglot실습하기

  1. gradle 프로젝트를 만들고 다음 의존성을 추가한다.
// 우리 코드에서 사용할 polyglot api.
// Truffle 프레임워크를 의존성으로 따라 가져온다.
// Truffle 프레임워크는 정의된 언어 컨텍스트의 구현체로 요청을 디스패치하여 해석한 후 Graph 를 만든다
implementation("org.graalvm.polyglot:polyglot:25.0.2")

// JS 폴리글랏 구현체. Truffle 에서 정의한 인터페이스에 맞게 구현되어 있으며, JS 문법을 Truffle 인터페이스로 바인딩한다고 볼 수 있다.
// 예를 들면 JS 를 해석하여 Truffle 인터페이스 A 로 바인딩하는 식.
implementation("org.graalvm.polyglot:js:25.0.2")

2-1. java 파일에서 js 코드 실행하기

import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;

public class JsFunctionCallDemo {
    public static void main(String[] args) {
        try (Context ctx = Context.newBuilder("js").build()) {
            ctx.eval("js", """
                function add(a, b) { return a + b; }
            """); // js code

            Value bindings = ctx.getBindings("js");
            Value add = bindings.getMember("add");

            int res = add.execute(1, 2).asInt();
            System.out.println(res); // 3
        }
    }
}

2-2. java 에서 js 파일 읽어서 실행하기
js file

// Hello.js
// gradle 프로젝트 최상위에 위치시키기
function add(a, b) {
    return a + b;
}

java file

import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Source;
import org.graalvm.polyglot.Value;

import java.io.File;

public class JsFileCallDemo {
    public static void main(String[] args) throws Exception {
        File jsFile = new File("hello.js");

        try (Context ctx = Context.newBuilder("js").build()) {
            Source src = Source.newBuilder("js", jsFile).build();
            ctx.eval(src);

            Value bindings = ctx.getBindings("js");
            Value add = bindings.getMember("add");

            int res = add.execute(10, 32).asInt();
            System.out.println(res); // 42
        }
    }
}


https://www.graalvm.org/latest/reference-manual/native-image/guides/build-polyglot-native-executable/?utm_source=chatgpt.com

GraalVM 으로 공유 라이브러리 AOT 컴파일

https://www.graalvm.org/latest/reference-manual/native-image/guides/build-native-shared-library/

Galahad 프로젝트

Galahad 는 GraalVM 기술이 JDK 에 포함되도록 하는 프로젝트이다.

profile
안녕하세요!

0개의 댓글