반대로 JVM 계열 언어를 일반적으로 컴파일해서 사용할 순 없나요?

가능합니다. JVM 언어도 바이트코드 대신 네이티브 기계어로 직접 컴파일(AOT) 해서 JVM 없이 실행할 수 있으며, 대표적인 도구가 GraalVM Native Image입니다.

기본 흐름 vs AOT 컴파일

[ 기본 — JVM 위에서 실행 ]
Hello.java → javac → Hello.class → JVM(Interpreter + JIT) → OS/CPU
                                    ↑ JVM 필요

[ AOT — 네이티브 바이너리로 변환 ]
Hello.java → javac → Hello.class → native-image → hello (실행파일) → OS/CPU 직접 실행
                                    ↑ GraalVM AOT       ↑ JVM 불필요

대표 도구: GraalVM Native Image

# 1. 일반적인 Java 컴파일
javac Hello.java

# 2. AOT 컴파일로 네이티브 바이너리 생성
native-image Hello

# 3. JVM 없이 직접 실행
./hello
항목JVM 실행Native Image
시작 속도수백 ms ~ 수 초수 ms ~ 수십 ms
메모리 사용많음 (JVM 오버헤드)적음
최대 성능JIT 워밍업 후 매우 빠름정적 최적화 수준
배포 크기JRE 포함 시 ~200MB바이너리 ~20~50MB
JVM 필요 여부필요불필요

다른 AOT 도구들

도구대상 언어특징
GraalVM Native ImageJava, Kotlin, Scala 등가장 대표적, Spring Boot 공식 지원
Kotlin/NativeKotlinLLVM 기반, iOS·임베디드 타겟 가능
Scala NativeScalaLLVM 기반 네이티브 컴파일

Kotlin/Native 예시

// hello.kt
fun main() {
    println("JVM 없이 실행!")
}
kotlinc-native hello.kt -o hello
./hello.kexe    # JVM 없이 직접 실행
Kotlin 소스
   │
   ├── kotlinc ────→ .class ──→ JVM 실행      (기본)
   │
   └── kotlinc-native → LLVM IR → 기계어 바이너리  (AOT)
                                    ↑ JVM 불필요

AOT의 한계

만능은 아니며, 트레이드오프가 존재합니다.

한계설명
리플렉션 제한런타임에 클래스 정보를 동적으로 탐색하는 기능이 제한됨 → 별도 설정 필요
동적 프록시 제한Spring AOP 등에서 사용하는 동적 프록시 일부 불가
빌드 시간 증가AOT 컴파일 자체가 수 분 이상 소요
JIT 최적화 부재런타임 프로파일링 기반 최적화가 없으므로 장시간 실행 시 JVM이 더 빠를 수 있음
디버깅 어려움네이티브 바이너리는 JVM 도구(jstack, jconsole 등) 사용 불가
성능
 ▲
 │          ┌── JVM (JIT 워밍업 후)
 │         ╱
 │        ╱
 │  ─────╱──── Native Image (일정한 성능)
 │  ╱
 │ ╱  ← JVM 초기 워밍업 구간
 └──────────────────────────► 시간

 짧은 실행: Native Image 유리 (서버리스, CLI)
 긴 실행:   JVM 유리 (장기 운영 서버)

실무 적용 사례

사례선택이유
AWS Lambda (서버리스)Native Image콜드 스타트 시간 최소화
CLI 도구Native Image즉시 시작, 배포 간편
마이크로서비스Native Image컨테이너 이미지 경량화
대규모 장기 운영 서버JVMJIT 최적화로 장기 성능 우수

Spring Boot 3 + GraalVM 예시

# Spring Boot에서 네이티브 빌드
./gradlew nativeCompile

# 결과: JVM 없이 실행 가능한 바이너리
./build/native/nativeCompile/my-app

# 시작 시간 비교
# JVM:           ~2.5초
# Native Image:  ~0.05초  (약 50배 빠름)

핵심 정리

JVM 언어도 GraalVM Native Image, Kotlin/Native 등을 통해 네이티브 바이너리로 AOT 컴파일할 수 있습니다. 이 경우 JVM 없이 실행 가능하고 시작 속도·메모리에서 큰 이점이 있지만, 리플렉션 제한과 JIT 최적화 부재라는 트레이드오프가 있습니다. 따라서 서버리스·CLI처럼 빠른 시작이 중요한 환경에서는 AOT, 장기 운영 서버에서는 JVM이 여전히 유리합니다.

CSLOG에서 보기