BACKEND.*/Server

JIT 컴파일러

초코푸딩 2024. 12. 22. 19:13

아무리 고성능이 요구될 때는 C, C++로 개발하는게 좋다고해도 요즘 컨퍼런스 가보면 엔진 구동하는 서버들도 java로 하는 경우를 많이 본거같은데 난 테스트해서 내 눈으로 봐야것다.. 같은 로직을 수행하는 C++ 앱이랑 java랑 개발해보고 리소스 차이 비교하고 성능 비교해서 증명해보는게 재밌을 것 같다. (같은 목적의 앱을 다른 언어로 구현하는건 굳이 필요 없으니까 해본 적이 없는데 해보면 재밌을 것 같다... java는 GrallVM으로 컴파일한거랑 그냥 jit컴파일해서 총세개비교  )

아래는 우아한 형제들에서 C++ 시스템 java로 전환한거 참고.. 

https://techblog.woowahan.com/20763/

 

 

**JIT 컴파일러(Just-In-Time Compiler)**는 프로그램 실행 중에 **바이트코드(Bytecode)**를 **기계어(Machine Code)**로 변환하여 프로그램 성능을 최적화하는 기술입니다. Java의 JVM(Java Virtual Machine)을 비롯해 여러 런타임 환경에서 사용됩니다.


1. JIT 컴파일러의 개념

  • 바이트코드(Bytecode):
    • Java 소스 코드를 컴파일하면 생성되는 중간 코드.
    • 플랫폼 독립적이며, JVM이 실행할 수 있는 코드입니다.
  • JIT 컴파일(Just-In-Time Compilation):
    • 바이트코드를 프로그램 실행 도중에 기계어로 변환하여 프로그램 성능을 향상시킵니다.
    • 정적 컴파일(예: C/C++)과 동적 해석(예: 인터프리터) 방식의 장점을 결합한 방식.

2. JIT 컴파일러의 동작 방식

  1. Java 프로그램 실행:
    • 바이트코드는 먼저 **인터프리터(Interpreter)**에 의해 실행됩니다.
    • 인터프리터는 한 줄씩 바이트코드를 해석하여 실행하므로 초기 실행 속도가 느립니다.
  2. JIT 컴파일 활성화:
    • JVM은 실행 중 자주 호출되는 핫스팟(Hotspot) 코드를 감지합니다.
    • 감지된 코드(핫스팟)는 JIT 컴파일러에 의해 기계어로 컴파일됩니다.
  3. 기계어 캐싱:
    • JIT 컴파일러가 생성한 기계어는 캐싱되며, 이후 호출 시 재사용됩니다.
    • 이 과정에서 실행 속도가 크게 향상됩니다.
  4. 최적화 수행:
    • JIT 컴파일러는 실행 중 수집된 데이터를 기반으로 **동적 최적화(Dynamic Optimization)**를 수행합니다.
    • 예: 인라인 함수 호출, 루프 전개(Loop Unrolling), 상수 폴딩(Constant Folding) 등.

3. JIT 컴파일러의 주요 최적화 기술

JIT 컴파일러는 다음과 같은 다양한 최적화 기술을 제공합니다:

  1. 인라인 확장(Inlining):
    • 자주 호출되는 함수를 호출하지 않고, 해당 코드를 호출 지점에 직접 삽입합니다.
    • 호출 오버헤드를 줄여줍니다.
  2. 루프 최적화:
    • 루프 전개(Loop Unrolling): 반복 횟수를 줄이기 위해 반복문 내부의 코드를 반복 실행 코드로 대체.
    • 루프 인바리언트 제거(Loop Invariant Code Motion): 루프 안에서 변하지 않는 코드를 밖으로 이동.
  3. 상수 폴딩(Constant Folding):
    • 실행 중 바뀌지 않는 값을 미리 계산하여 최적화.
  4. 분기 예측(Branch Prediction):
    • 조건문 실행 시 자주 실행되는 분기를 우선적으로 최적화.
  5. 메모리 최적화:
    • 객체 할당 및 GC(가비지 컬렉션) 성능 최적화.

4. JIT 컴파일러의 장점

  1. 실행 속도 향상:
    • 기계어로 변환된 코드가 캐싱되어 반복 실행 시 속도가 빨라집니다.
  2. 실시간 최적화:
    • 프로그램 실행 중 수집된 데이터를 바탕으로 코드 최적화를 수행합니다.
  3. 플랫폼 독립성 유지:
    • 바이트코드는 플랫폼 독립적이므로, JIT 컴파일러는 각 플랫폼에서 적합한 기계어로 변환합니다.
  4. 코드 크기 절감:
    • 자주 사용되지 않는 코드(콜드 스팟)는 컴파일하지 않으므로 메모리 사용량을 줄입니다.

5. JIT 컴파일러의 단점

  1. 초기 실행 지연:
    • 처음 실행 시 인터프리터로 실행되므로 속도가 느립니다.
  2. 런타임 오버헤드:
    • JIT 컴파일링 과정이 프로그램 실행 중에 발생하므로, 추가적인 CPU 및 메모리 리소스를 사용합니다.
  3. 메모리 사용 증가:
    • 기계어 코드 캐싱으로 인해 메모리 사용량이 증가할 수 있습니다.

6. JIT 컴파일러와 AOT 컴파일러 비교

특징JIT 컴파일러AOT 컴파일러(Ahead-of-Time)

컴파일 시점 실행 시(Runtime) 컴파일 시(Compile Time)
최적화 수준 런타임 데이터를 기반으로 최적화 정적 분석을 기반으로 최적화
초기 실행 속도 느림 (인터프리터 실행 후 JIT 수행) 빠름 (기계어로 미리 컴파일)
플랫폼 독립성 바이트코드 기반으로 유지 플랫폼별로 별도의 바이너리 생성 필요
적용 사례 JVM(Java), .NET CLR C/C++, Android NDK

7. JIT 컴파일러의 JVM에서의 역할

Java에서 JIT 컴파일러는 HotSpot JVM의 핵심 기능 중 하나입니다.
HotSpot JVM에는 다음과 같은 두 가지 JIT 컴파일러가 포함되어 있습니다:

  1. C1 컴파일러(클라이언트 컴파일러):
    • 빠른 초기 실행을 목표로 최적화를 최소화하여 동작.
    • 적합한 환경: GUI 기반 애플리케이션 등.
  2. C2 컴파일러(서버 컴파일러):
    • 실행 성능을 극대화하기 위해 고급 최적화 수행.
    • 적합한 환경: 대규모 서버 애플리케이션.

8. JIT 컴파일러가 사용되는 다른 환경

  1. .NET Framework:
    • CLR(Common Language Runtime)에서 CIL(Common Intermediate Language)을 기계어로 변환.
  2. JavaScript 엔진:
    • 브라우저의 V8(JavaScript 엔진)은 JIT 컴파일을 사용하여 JavaScript를 최적화.
  3. Python:
    • PyPy와 같은 일부 Python 런타임에서도 JIT 컴파일 사용.

9. 결론

  • JIT 컴파일러는 실행 중 동적으로 최적화를 수행하여 Java 애플리케이션의 성능을 향상시키는 핵심 기술입니다.
  • 실시간 데이터 기반 최적화 덕분에 정적 컴파일러보다 높은 성능을 제공할 수 있지만, 초기 실행 지연 및 런타임 오버헤드와 같은 단점도 존재합니다.
  • 프로그램의 특성에 따라 JIT과 AOT를 조합하여 최적의 성능을 도모할 수 있습니다.

Java 코드가 실행될 때 최종적으로 **기계어(Machine Code)**로 번역되는 작업을 담당하는 컴파일러는 바로 JIT(Just-In-Time) 컴파일러입니다. 하지만 이 과정을 좀 더 세부적으로 살펴보면, JVM(Java Virtual Machine)에서 바이트코드를 기계어로 번역할 때 여러 단계와 기술이 사용됩니다. 아래에서 이를 자세히 설명하겠습니다.


1. JVM에서 기계어로 변환 과정

Java 프로그램이 실행되는 과정은 다음과 같습니다:

  1. Java 코드 → 바이트코드 변환 (Javac 컴파일러)
    • 개발자가 작성한 Java 소스 파일(.java)은 Javac 컴파일러에 의해 **바이트코드(Bytecode)**로 컴파일됩니다.
    • 이 바이트코드는 .class 파일에 저장되며, 플랫폼 독립적인 형태입니다.
  2. 바이트코드 실행 → 인터프리터 또는 JIT 컴파일러
    • JVM은 .class 파일의 바이트코드를 읽어 실행합니다.
    • **초기에는 인터프리터(Interpreter)**가 바이트코드를 한 줄씩 실행합니다.
    • 실행 중, JIT 컴파일러가 동작하여 자주 실행되는 코드(핫스팟 코드)를 기계어로 컴파일합니다.
  3. 기계어로 변환
    • JIT 컴파일러는 바이트코드를 CPU가 이해할 수 있는 **기계어(Machine Code)**로 변환하여 실행합니다.
    • 변환된 기계어는 캐싱되어 이후 반복 실행 시 재사용됩니다.

2. JIT 컴파일러 vs 네이티브 컴파일러

기계어로 변환할 때 사용되는 컴파일러는 JIT 컴파일러이며, 이는 런타임에 동작하는 동적 컴파일러입니다.
Java와 같이 바이트코드 기반의 플랫폼은 런타임 최적화를 위해 JIT을 사용하지만, 다른 환경에서는 네이티브 컴파일러가 기계어 변환을 수행합니다.

구분JIT 컴파일러네이티브 컴파일러
컴파일 시점 런타임(Runtime) 컴파일 타임(Compile Time)
주요 역할 바이트코드 → 기계어 변환 소스코드 → 기계어 변환
사용 환경 Java, .NET, JavaScript 엔진 등 C, C++, Rust 등 정적 언어

3. 최종적으로 번역을 담당하는 컴파일러

Java 환경에서 기계어로 번역을 담당하는 컴파일러는 JIT 컴파일러입니다.
JIT 컴파일러는 JVM 내부에서 실행되며, 바이트코드를 분석하고 자주 사용되는 코드를 최적화하여 기계어로 변환합니다.


4. Java에서 사용되는 JIT 컴파일러 종류

JVM에는 다음과 같은 두 가지 JIT 컴파일러가 포함되어 있습니다:

  1. C1 컴파일러 (Client Compiler)
    • 간단한 최적화 수행.
    • GUI 기반 애플리케이션이나 빠른 초기 실행이 필요한 환경에 적합.
  2. C2 컴파일러 (Server Compiler)
    • 고급 최적화를 수행하여 실행 성능을 극대화.
    • 대규모 서버 애플리케이션 환경에 적합.

이러한 컴파일러는 HotSpot JVM에 포함되어 있으며, 핫스팟 코드(자주 실행되는 코드)를 탐지하여 최적화합니다.


5. 네이티브 코드와 관련된 기술

Java 프로그램이 실행될 때 최종적으로 기계어로 번역되지만, 이 과정에서 네이티브 코드를 활용하는 경우도 있습니다:

  1. JNI (Java Native Interface)
    • Java에서 네이티브 코드(C/C++)를 호출할 때 사용되는 인터페이스.
    • 고성능 작업(예: 하드웨어 제어)을 위해 네이티브 코드를 직접 호출.
  2. AOT (Ahead-Of-Time Compilation)
    • JIT 대신 실행 전에 바이트코드를 기계어로 컴파일.
    • 대표적으로 GraalVM의 Native Image가 있습니다.

6. 결론

Java에서 최종적으로 기계어로 번역되는 작업을 담당하는 컴파일러는 JIT 컴파일러입니다.
JIT 컴파일러는 런타임 중 동적으로 바이트코드를 기계어로 변환하며, 프로그램 성능을 최적화합니다.
Java 외의 정적 언어(C/C++)에서는 네이티브 컴파일러가 이 역할을 수행합니다.

따라서 Java 실행 환경에서 기계어 변환은 다음 흐름을 따릅니다: Java 소스 코드 → 바이트코드(Javac) → 기계어(JIT 컴파일러).

 
4o

나의 말:

그럼 C랑 C++보다 자바가 느리고 메모리 많이 잡아먹는거는 JVM이랑 JIT 컴파일러 때문에 동적으로 최적화하는게 있어서 그런거네?
 

ChatGPT의 말:

ChatGPT

정확히 그렇습니다!
Java는 **JVM(Java Virtual Machine)**과 JIT(Just-In-Time) 컴파일러를 사용해 실행되기 때문에, CC++ 같은 정적 컴파일 언어에 비해 초기 실행 속도가 느리고 메모리 사용량이 더 많을 수 있습니다. 하지만 이 과정은 플랫폼 독립성, 동적 최적화, 개발 편의성이라는 장점을 제공하기 위해 설계된 트레이드오프입니다.

아래에서 Java가 C/C++에 비해 느리고 메모리를 더 많이 사용하는 이유를 상세히 설명하겠습니다.


1. Java가 느린 이유

(1) JVM의 오버헤드

  • Java 코드는 먼저 JVM에서 바이트코드로 해석된 뒤 실행됩니다.
  • JVM은 추상화된 실행 환경을 제공하기 때문에, C/C++처럼 바로 기계어로 실행되지 않습니다.
  • JVM이 수행하는 작업:
    • 바이트코드 해석(초기 단계).
    • 런타임에 클래스 로딩 및 바이트코드 검증.
    • JIT 컴파일로 최적화된 기계어 생성.

(2) JIT 컴파일의 지연

  • Java는 프로그램 실행 중에 JIT 컴파일을 통해 바이트코드를 기계어로 변환합니다.
  • 이 과정에서 추가적인 CPU 및 메모리 자원이 소모됩니다.
  • 특히, 프로그램 초기 실행 시 JIT 컴파일러가 아직 모든 코드를 최적화하지 않았으므로, 초기 실행 속도가 느릴 수 있습니다.

(3) 가비지 컬렉션(Garbage Collection)

  • Java는 메모리를 자동으로 관리하기 위해 **가비지 컬렉션(GC)**을 사용합니다.
  • GC는 주기적으로 메모리를 해제하는데, 이 작업이 실행 중 성능에 영향을 미칠 수 있습니다.
  • 반면, C/C++은 개발자가 메모리를 직접 관리하므로, 메모리 관리로 인한 성능 오버헤드가 없습니다(다만, 메모리 관리 실수로 인해 문제가 발생할 수 있음).

2. Java가 메모리를 많이 사용하는 이유

(1) JVM이 사용하는 메모리

  • Java는 JVM을 통해 실행되므로 JVM 자체가 사용하는 메모리가 추가적으로 필요합니다:
    • Heap Memory: 객체가 저장되는 공간.
    • Stack Memory: 각 스레드에서 사용하는 메모리.
    • Metaspace: 클래스 메타데이터가 저장되는 공간.
  • JVM은 애플리케이션 실행 전후로 메모리를 예약하고 관리하기 때문에, 기본 메모리 사용량이 높을 수 있습니다.

(2) 동적 메모리 할당

  • Java는 객체를 동적으로 생성하며, new 키워드를 통해 생성된 객체는 힙(heap) 메모리에 저장됩니다.
  • C/C++에서는 객체를 스택(stack)에 저장하거나, 필요할 경우에만 동적으로 할당하는 선택이 가능하므로 메모리 사용량이 더 적을 수 있습니다.

(3) 가비지 컬렉션

  • GC는 주기적으로 메모리를 해제하지만, 활성 객체를 이동하거나 압축하는 작업이 추가적인 메모리 사용량을 발생시킬 수 있습니다.
  • 반대로 C/C++에서는 필요할 때 명시적으로 메모리를 해제하므로 이러한 오버헤드가 없습니다.

(4) 클래스와 라이브러리 로딩

  • JVM은 실행 시 필요한 클래스와 라이브러리를 메모리에 로드합니다.
  • Java 애플리케이션은 풍부한 표준 라이브러리를 활용하므로, 추가 메모리가 필요합니다.

3. C와 C++이 빠르고 메모리를 덜 사용하는 이유

(1) 정적 컴파일

  • C/C++ 코드는 실행 전에 **네이티브 컴파일러(GCC, Clang 등)**에 의해 기계어로 변환됩니다.
  • 실행 시 별도의 해석 단계가 필요 없으므로 빠르게 실행됩니다.

(2) 직접적인 메모리 관리

  • C/C++은 개발자가 메모리 할당과 해제를 직접 제어하므로, 필요 이상으로 메모리를 사용하지 않습니다.
  • 예를 들어, 스택 메모리는 함수 호출 시 자동으로 생성되고, 함수가 끝나면 자동으로 해제됩니다.
  • 하지만, 직접 관리로 인해 메모리 누수 또는 더블 해제(double free) 같은 문제가 발생할 수 있습니다.

(3) 런타임 환경의 부재

  • C/C++은 런타임 환경 없이 네이티브 환경에서 실행되므로 JVM이 차지하는 메모리와 CPU 오버헤드가 없습니다.

4. Java의 성능과 메모리 사용량 개선

(1) JIT 컴파일과 동적 최적화

  • JIT 컴파일러는 실행 중 최적화를 수행하므로, 장기적으로 보면 성능이 개선됩니다.
  • Java 애플리케이션은 초기 실행 속도가 느릴 수 있지만, 핫스팟 코드가 최적화된 이후에는 C/C++에 가까운 성능을 낼 수 있습니다.

(2) 가비지 컬렉션 튜닝

  • JVM 옵션을 통해 GC의 동작을 최적화하여 메모리 사용량을 줄이고 성능을 개선할 수 있습니다:
    bash
    코드 복사
    java -Xms512m -Xmx1024m -XX:+UseG1GC

(3) AOT 컴파일

  • GraalVM과 같은 기술을 사용하면 Java 코드를 실행 전에 **기계어로 컴파일(AOT, Ahead-Of-Time)**하여 실행 속도를 개선할 수 있습니다.

5. 결론

  • Java가 C/C++보다 느리고 메모리를 더 많이 사용하는 이유:
    • JVM의 실행 환경과 JIT 컴파일러의 동적 최적화 과정.
    • 가비지 컬렉션으로 인한 메모리 관리 오버헤드.
    • 플랫폼 독립성을 유지하기 위한 추가 추상화 계층.
  • Java의 장점:
    • 플랫폼 독립성.
    • 자동 메모리 관리.
    • JIT 컴파일러로 장기적인 성능 최적화.

C/C++은 빠르고 메모리를 적게 사용하지만, 개발자에게 메모리 관리의 책임이 부여됩니다. Java는 편의성과 안정성을 제공하며, 성능은 JIT 컴파일러와 GC 최적화를 통해 개선할 수 있습니다. 목적과 요구 사항에 따라 적합한 언어를 선택하는 것이 중요합니다.

 
4o

나의 말:

그럼 그랄VM인가? 그거는 그러면은 C나 C++처럼 미리 기계어로 컴파일 해놓고 쓰는거네? 그 통켓에 JSP 미리 다 컴파일 해놓고 그래서 그 어플리케이션이 이미 떠있으면은 이제 변경이 안되는 거 있잖아 그러면 그것도 그랄VM 같은거야? 그 막 개발모드랑 운영모드에 따라서 프리컴파일 옵션 있잖아
 

ChatGPT의 말:

ChatGPT

**네, GraalVM의 AOT 컴파일(Ahead-of-Time Compilation)**과 JSP의 프리컴파일 옵션은 모두 미리 코드를 컴파일해서 실행 시 성능을 최적화하는 방식이라는 공통점이 있습니다. 하지만, 각각의 동작 방식과 목적은 약간 다릅니다. 아래에서 GraalVM과 JSP 프리컴파일의 개념과 연관성을 정리해 보겠습니다.


1. GraalVM과 AOT 컴파일

GraalVM이란?

  • GraalVM은 Java 생태계를 확장하는 고성능 런타임 환경으로, JVM(Java Virtual Machine)과 네이티브 이미지를 지원합니다.
  • GraalVM은 JIT(Just-In-Time) 컴파일러뿐만 아니라, AOT(Ahead-of-Time) 컴파일러를 제공합니다.

AOT 컴파일

  • AOT 컴파일은 프로그램을 실행 전에 기계어로 변환합니다.
    • Java 바이트코드를 네이티브 바이너리로 미리 컴파일해서 실행 시 JVM 없이 바로 실행할 수 있습니다.
    • 예를 들어, HelloWorld.class 파일을 GraalVM의 Native Image로 컴파일하면 실행 가능한 바이너리 파일(HelloWorld)이 생성됩니다.
    • 결과적으로 실행 시 JVM 로딩, 클래스 로딩, JIT 컴파일링 같은 오버헤드가 사라지므로 실행 속도가 빨라집니다.

GraalVM의 특징

  • 정적 실행 환경: GraalVM의 AOT 컴파일을 통해 생성된 애플리케이션은 Java의 동적 기능(예: 리플렉션, 동적 클래스 로딩)을 제한적으로 지원.
  • 메모리 절감: JVM이 필요 없으므로 메모리 사용량이 크게 줄어듭니다.
  • 초기 실행 속도 향상: 미리 컴파일된 바이너리를 실행하므로 초기 실행 속도가 매우 빠릅니다.

예시 (GraalVM Native Image 생성)

bash
코드 복사
# GraalVM 설치 후 AOT 컴파일 실행 native-image HelloWorld

결과:

bash
코드 복사
./helloworld

2. JSP 프리컴파일

JSP의 컴파일 과정

  • JSP(Java Server Pages)는 HTML과 Java 코드가 혼합된 형태로, 톰캣 같은 애플리케이션 서버에서 실행되기 전에 Servlet으로 변환되고, 변환된 Servlet 클래스가 컴파일되어 실행됩니다.
    • JSP → Servlet(Java 파일) → 클래스 파일 → 실행.

JSP의 프리컴파일

  • 프리컴파일은 JSP 파일을 톰캣이 실행하기 전에 미리 Servlet과 클래스 파일로 변환하는 작업입니다.
  • 이 과정은 다음과 같은 이유로 활용됩니다:
    1. 성능 향상: JSP 파일을 실행 시 컴파일하면 서버 초기 요청 처리 시간이 길어질 수 있으므로, 미리 컴파일하여 이 시간을 줄입니다.
    2. 운영 모드에서 안정성 보장: 미리 컴파일하면 JSP가 정상적으로 컴파일되고 실행 가능한 상태인지 확인할 수 있습니다.
    3. 동적 수정 방지: 운영 모드에서 JSP가 수정되지 않도록 보장.

프리컴파일 방식

  • 개발 모드:
    • JSP는 첫 요청 시 컴파일됩니다.
    • 개발자는 JSP 파일을 수정한 뒤 서버를 재시작하지 않고도 바로 반영할 수 있습니다.
  • 운영 모드:
    • 모든 JSP를 미리 컴파일하여 배포합니다.
    • 톰캣에서는 jspc(JSP Compiler)를 사용해 JSP를 미리 Servlet(Java 파일)로 변환한 뒤 컴파일합니다.

톰캣 JSP 프리컴파일 명령

bash
코드 복사
# 톰캣 JSPC를 사용한 프리컴파일 $CATALINA_HOME/bin/jspc.sh -webapp /path/to/webapp -d /path/to/output

프리컴파일된 JSP는 *.class 파일로 저장되며, 톰캣은 요청 시 이를 바로 실행합니다.


3. GraalVM AOT 컴파일 vs JSP 프리컴파일 비교

특징GraalVM AOT 컴파일JSP 프리컴파일
목적 Java 애플리케이션의 기계어 변환 및 성능 최적화 JSP 파일을 미리 Servlet(Java 파일)로 변환
컴파일 시점 애플리케이션 배포 시 웹 애플리케이션 배포 시
결과물 네이티브 바이너리 (플랫폼 의존적) 컴파일된 Servlet 클래스 파일
운영 환경 JVM 없이 실행 가능 여전히 JVM 및 톰캣 필요
동적 변경 가능성 제한적(네이티브 이미지는 동적 기능 일부 제한됨) 운영 중 JSP 수정 가능 여부는 설정에 따라 다름

4. 개발 모드와 운영 모드의 프리컴파일 옵션

개발 모드

  • JSP 파일을 요청 시 컴파일하는 방식이 일반적입니다.
  • 서버 재시작 없이 변경된 내용을 즉시 반영 가능.
  • 톰캣 설정 파일에서 development=true로 설정:
    xml
    코드 복사
    <Context reloadable="true" />

운영 모드

  • JSP를 미리 컴파일하여 배포.
  • 수정된 JSP 파일은 서버를 재시작하거나 다시 컴파일해야 반영.
  • 톰캣 설정에서 프리컴파일된 JSP를 사용:
    xml
    코드 복사
    <Context> <Loader delegate="true" /> </Context>

5. 결론

  • GraalVM:
    • GraalVM은 Java 코드를 미리 기계어로 변환하여, JVM 없이 실행 가능한 네이티브 바이너리를 생성합니다.
    • 실행 속도와 메모리 사용량을 최적화하며, 운영 모드에서 변경이 불가능한 정적인 실행 환경을 만듭니다.
  • JSP 프리컴파일:
    • JSP를 미리 Servlet 및 클래스 파일로 변환하여 톰캣에서 바로 실행 가능하도록 만듭니다.
    • 운영 모드에서 성능을 최적화하며, JSP 수정이 제한됩니다.

결론적으로, 두 기술 모두 운영 환경에서 안정성과 성능 최적화를 목표로 하지만, GraalVM은 네이티브 바이너리 생성, JSP 프리컴파일은 웹 애플리케이션 내 JSP 실행 최적화에 초점이 맞춰져 있습니다.

 
4o

나의 말:

헐, 그러면 그랄VM을 사용하면 자바로 개발했어도 C나 C++처럼 쓸 수 있는거야?
 

ChatGPT의 말:

ChatGPT

네, GraalVM의 AOT(Ahead-of-Time) 컴파일을 사용하면, Java로 개발된 애플리케이션을 C나 C++처럼 네이티브 바이너리로 변환해서 실행할 수 있습니다.
즉, Java 애플리케이션이 JVM 없이도 독립적으로 실행 가능하게 만들어주는 것입니다. 이것은 GraalVM의 Native Image 기능을 통해 가능합니다.


1. GraalVM으로 Java를 C/C++처럼 사용하는 방법

GraalVM의 Native Image

  • Native Image는 Java 애플리케이션을 실행 전에 기계어로 변환하여, 독립 실행 가능한 네이티브 바이너리를 생성합니다.
  • 생성된 바이너리는 JVM이 필요 없으므로 C/C++ 프로그램처럼 동작합니다.
    • 단일 실행 파일(.exe, Linux의 ELF 파일 등)로 배포 가능.
    • JVM의 메모리 관리나 런타임 오버헤드가 제거됩니다.

GraalVM Native Image의 주요 특징

  1. JVM 불필요:
    • 생성된 네이티브 바이너리는 JVM 없이 실행됩니다.
    • Java 런타임 환경(JRE)을 설치할 필요가 없습니다.
  2. 빠른 시작 시간:
    • 미리 컴파일된 기계어 코드로 실행되므로 애플리케이션 시작 속도가 크게 향상됩니다.
    • JVM의 클래스 로딩, JIT 컴파일 등이 필요 없기 때문.
  3. 메모리 사용량 절감:
    • JVM 메모리 관리(Garbage Collection 등)가 제거되며, 네이티브 코드가 더 적은 메모리를 사용합니다.
  4. 배포 간소화:
    • 네이티브 실행 파일로 패키징되므로, 실행 환경에 종속적이지 않습니다.
    • 컨테이너나 서버 환경에서 경량 애플리케이션으로 사용 가능.

2. GraalVM Native Image 생성 방법

예제 코드

java
코드 복사
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, GraalVM Native Image!"); } }

Native Image 생성 과정

  1. GraalVM 설치:
  2. Native Image 컴파일러 설치:
  3. bash
    코드 복사
    gu install native-image
  4. Native Image 생성:
  5. bash
    코드 복사
    native-image -jar HelloWorld.jar
  6. 생성된 네이티브 바이너리 실행:
  7. bash
    코드 복사
    ./helloworld

결과

  • 출력: Hello, GraalVM Native Image!
  • ./helloworld 파일은 JVM 없이 실행 가능한 네이티브 실행 파일입니다.

3. GraalVM의 장점과 단점

장점

  1. 빠른 실행 속도:
    • 네이티브 바이너리는 JVM 초기화나 클래스 로딩이 필요 없으므로 실행 속도가 빠릅니다.
  2. 낮은 메모리 사용량:
    • 네이티브 애플리케이션은 JVM 런타임을 제거하여 메모리 사용량을 크게 줄일 수 있습니다.
  3. 플랫폼 독립적 개발:
    • Java로 개발하여 GraalVM으로 플랫폼별 네이티브 바이너리(Linux, macOS, Windows)로 컴파일 가능합니다.
  4. C/C++과 유사한 배포 방식:
    • 단일 실행 파일로 배포 가능하며, 운영 체제에 설치된 JVM에 의존하지 않습니다.

단점

  1. 동적 기능 제한:
    • GraalVM은 리플렉션, 동적 클래스 로딩, 동적 프록시 같은 Java의 동적 기능을 제한적으로 지원합니다.
    • 이러한 기능을 사용하려면 명시적으로 설정해야 하며, 복잡한 설정이 필요할 수 있습니다.
  2. 빌드 시간 증가:
    • 네이티브 이미지를 생성하는 과정은 일반 Java JAR 파일 생성보다 시간이 더 오래 걸립니다.
  3. 호환성 이슈:
    • 일부 Java 라이브러리나 프레임워크는 GraalVM Native Image에서 동작하지 않을 수 있습니다.
    • Spring Boot, Micronaut 등은 GraalVM을 지원하도록 최적화되어 있지만, 모든 Java 애플리케이션이 바로 호환되지는 않습니다.

4. JSP나 웹 애플리케이션에서의 활용

GraalVM으로 웹 애플리케이션을 네이티브로 컴파일

  • Spring Boot, Micronaut 같은 프레임워크를 사용하면 GraalVM으로 REST API나 웹 애플리케이션도 네이티브 이미지로 컴파일 가능합니다.
  • 이를 통해 톰캣 같은 애플리케이션 서버를 JVM 없이 실행할 수도 있습니다.

Spring Boot 예시

  1. spring-native 의존성 추가:
  2. xml
    코드 복사
    <dependency> <groupId>org.springframework.experimental</groupId> <artifactId>spring-native</artifactId> <version>0.11.0</version> </dependency>
  3. Native Image 생성:
  4. bash
    코드 복사
    mvn package -Pnative

5. GraalVM의 실제 활용 사례

  • 서버리스(Serverless): 빠른 시작 속도와 낮은 메모리 사용량이 필요한 서버리스 환경에 적합.
  • 컨테이너: 네이티브 바이너리를 생성하여 가벼운 컨테이너 이미지를 만들 수 있습니다.
  • IoT 디바이스: 제한된 메모리와 CPU를 사용하는 환경에서 GraalVM 네이티브 이미지는 큰 장점을 가집니다.

6. 결론

  • GraalVM을 사용하면 Java 애플리케이션을 C/C++처럼 독립 실행 파일로 만들어 실행할 수 있습니다.
  • JVM의 오버헤드 없이 실행 가능하며, 메모리 사용량이 적고 빠른 시작 속도를 제공합니다.
  • 그러나 동적 기능이 제한되고, 일부 라이브러리와의 호환성 문제가 있을 수 있으므로 사용 시 주의가 필요합니다.
  • GraalVM은 특히 서버리스, 컨테이너 환경, 경량 애플리케이션 배포에 적합하며, Java 생태계를 확장하는 강력한 도구입니다.
 
4o

나의 말:

근데 그러면 그랄 VM을 쓰면 GCE가 동작을 안 하잖아. 그러면 씨나 씨뿔뿔처럼 메모리 할당 해제하는 그런 코드가 없는 건데 그게 잘 돌아가? 메모리가 해제가 돼? 어떻게 해제가 돼?
 

ChatGPT의 말:

ChatGPT

GraalVM의 Native Image를 사용하면 JVM 없이 실행되지만, **Garbage Collector(GC)**는 여전히 동작합니다. 이는 GraalVM의 Native Image에서도 **Java의 메모리 관리 방식(가비지 컬렉션)**을 그대로 활용하기 때문입니다. 따라서, GraalVM Native Image는 C나 C++처럼 메모리를 직접 관리하는 방식이 아니라, Java 스타일의 자동 메모리 관리를 지원합니다.


1. GraalVM Native Image에서 메모리 관리

가비지 컬렉션(GC) 지원

  • GraalVM Native Image는 JVM을 제거했지만, 여전히 **가비지 컬렉션(GC)**을 지원합니다.
  • Native Image는 Java 애플리케이션에서 사용하는 힙 메모리 관리와 객체 참조 추적을 위한 **경량 런타임(Lightweight Runtime)**을 포함합니다.
  • 이 런타임이 가비지 컬렉션을 수행하여, 동적으로 생성된 객체를 안전하게 해제합니다.

메모리 관리 방식

  • Java 애플리케이션과 동일하게, 객체는 더 이상 참조되지 않을 때 자동으로 가비지 컬렉터에 의해 메모리에서 해제됩니다.
  • Native Image 생성 시, GraalVM은 최소한의 GC 구현을 런타임에 포함하여 메모리 누수 방지자동 해제를 보장합니다.

GC 종류

  • GraalVM Native Image는 가벼운 GC 런타임을 제공하며, 기본적으로 Serial GC 같은 간단한 GC를 포함합니다.
  • 더 고급 GC를 선택하는 것도 가능하지만, 네이티브 이미지에서는 메모리 관리가 간소화된 경량 GC를 사용하는 것이 일반적입니다.

2. GraalVM Native Image와 C/C++의 메모리 관리 차이

특징GraalVM Native ImageC/C++
메모리 할당 방식 Java 스타일(동적 객체는 힙에 생성, 스택 메모리 자동 관리) malloc/free 또는 new/delete 사용
메모리 해제 가비지 컬렉터(GC)가 자동으로 메모리 해제 개발자가 명시적으로 해제해야 함
메모리 누수 가능성 매우 낮음(가비지 컬렉션으로 누수 방지) 메모리 해제 실수로 인해 누수가 발생 가능
런타임 오버헤드 GC 작업으로 인한 오버헤드 있음 명시적 해제이므로 GC 오버헤드 없음

3. GraalVM Native Image에서 메모리 해제가 잘 되는 이유

  1. GC를 포함한 경량 런타임 제공
    • GraalVM은 Native Image에 가벼운 런타임 환경을 포함하며, 이 런타임에는 기본적인 GC가 내장되어 있습니다.
    • 따라서 Native Image로 변환된 애플리케이션도 Java처럼 자동으로 메모리가 관리됩니다.
  2. 객체 참조 추적
    • GraalVM 런타임은 Java 애플리케이션에서 사용되는 객체 참조를 추적합니다.
    • 참조가 끊어진 객체는 GC가 감지하여 메모리를 해제합니다.
  3. 동적 메모리 할당이 제한적
    • GraalVM Native Image는 동적 클래스 로딩이나 리플렉션 같은 Java의 일부 동적 기능을 제한합니다.
    • 이러한 제한 덕분에 메모리 관리가 더 간단해지고, 불필요한 메모리 누수 가능성이 줄어듭니다.

4. GraalVM Native Image에서의 메모리 최적화

(1) AOT 컴파일 최적화

  • GraalVM은 AOT 컴파일 중 **필요하지 않은 클래스와 메서드를 제거(Dead Code Elimination)**합니다.
  • 이를 통해 런타임 메모리 사용량이 줄어듭니다.

(2) Native Image 옵션

  • GraalVM은 Native Image를 생성할 때 다양한 메모리 관련 옵션을 제공합니다:
    bash
    코드 복사
    native-image --initialize-at-build-time -H:+UseSerialGC
    • --initialize-at-build-time: 런타임에 초기화할 필요가 없는 클래스와 객체를 미리 초기화하여 메모리 사용량 감소.
    • -H:+UseSerialGC: Serial GC 사용(간단한 GC 알고리즘).

(3) 필요하지 않은 힙 메모리 제거

  • GraalVM은 실행 중 필요하지 않은 힙 메모리를 조기에 해제할 수 있도록 최적화됩니다.

5. GraalVM Native Image에서 메모리 누수가 발생할 가능성

  • Java는 자동 메모리 관리를 제공하므로, 메모리 누수 가능성이 낮습니다.
  • 그러나 Native Image에서 GC가 완벽하지 않거나, 네이티브 코드(JNI 등)와의 상호작용이 잘못되면 메모리 누수가 발생할 수 있습니다.
  • 네이티브 코드와 통합 시, C/C++ 스타일의 메모리 관리 방식이 필요할 수 있습니다.

6. 실제로 잘 돌아갈까?

네, GraalVM Native Image는 메모리 해제가 잘 이루어지며, 안정적으로 동작합니다.
다음과 같은 이유로 Java 애플리케이션이 GraalVM Native Image로 변환되어도 메모리 관리 문제가 적습니다:

  1. 가비지 컬렉터가 포함되어 객체를 안전하게 해제.
  2. GraalVM의 최적화가 불필요한 메모리 사용을 줄여줌.
  3. Java 언어 특성상 메모리를 직접 관리하지 않아도 안전함.

7. 결론

  • GraalVM Native Image는 JVM 없이 실행되지만, 경량 런타임과 GC를 통해 Java 스타일의 자동 메모리 관리를 제공합니다.
  • 따라서 C/C++처럼 명시적으로 메모리를 해제하지 않아도 안정적이고 효율적인 메모리 관리가 가능합니다.
  • 메모리 해제가 잘 되는 이유는 GraalVM이 자동 메모리 관리와 최적화를 포함한 런타임을 제공하기 때문입니다.
  • 그러나 네이티브 코드와 통합하거나 특정 최적화가 필요한 경우에는 추가적인 주의가 필요합니다.
 
4o

나의 말:

헐, 나 그럼 완전 충격받는데 그러면 C랑 C++로 굳이 개발 안하고 Java로 개발한 다음에 그 뭐냐, GraalVM 해가지고 하는게 오히려 더 개발자로 인한 실수를 줄여주고 더 장점 많은 거 아닌가?
 

ChatGPT의 말:

ChatGPT

네, 맞습니다!
Java와 GraalVM Native Image를 활용하면 C/C++에서 발생할 수 있는 여러 메모리 관리 실수를 줄이고, 개발 생산성을 높이면서도 네이티브 환경에서 실행되는 고성능 애플리케이션을 만들 수 있습니다. 이를 통해 Java의 강점C/C++ 스타일의 성능과 배포 방식을 모두 얻을 수 있습니다.


1. Java + GraalVM의 장점

(1) 메모리 관리의 안정성

  • **Java의 자동 메모리 관리(GC)**를 유지하면서, C/C++처럼 네이티브 바이너리로 컴파일할 수 있습니다.
  • C/C++에서 흔히 발생하는 메모리 누수, 더블 해제(double free), 버퍼 오버플로우 같은 문제가 크게 줄어듭니다.

(2) 빠른 개발 속도

  • Java는 풍부한 표준 라이브러리와 생산성을 높이는 도구(IDE, 빌드 도구 등)가 많아 개발 속도가 빠릅니다.
  • GraalVM으로 컴파일하면 Java의 생산성을 유지하면서 C/C++ 수준의 실행 성능을 얻을 수 있습니다.

(3) 플랫폼 독립성 + 네이티브 성능

  • Java 코드는 플랫폼 독립적으로 작성되지만, GraalVM Native Image로 컴파일하면 플랫폼별 네이티브 바이너리로 배포할 수 있습니다.
  • Docker 같은 컨테이너 환경이나 경량 서버리스 환경에서 유리합니다.

(4) 빠른 실행 속도

  • GraalVM Native Image는 JIT 없이도 빠른 초기 실행 속도를 제공합니다.
  • JVM 초기화, 클래스 로딩 등의 오버헤드가 제거됩니다.

(5) 낮은 메모리 사용량

  • JVM 없이 실행되므로 메모리 사용량이 줄어듭니다.
  • 특히 컨테이너화된 환경에서 메모리 최적화가 중요한 경우 매우 유리합니다.

(6) 복잡한 포인터 연산 없음

  • Java는 C/C++처럼 복잡한 포인터 연산이 없으므로, 개발자 실수로 인한 보안 취약점(예: Use-After-Free, Null Pointer Dereference 등)을 방지합니다.

2. GraalVM을 활용할 때 C/C++ 대신 Java를 선택해야 하는 이유

(1) C/C++의 한계

  • 메모리 관리 부담: 개발자가 직접 메모리를 관리해야 하므로, 실수로 메모리 누수나 해제 문제를 일으킬 가능성이 큽니다.
  • 보안 취약점: 포인터 연산, 버퍼 오버플로우 등으로 인해 보안 문제가 발생할 가능성이 많습니다.
  • 생산성 저하: Java에 비해 개발 속도가 느리고, 디버깅과 유지보수가 어렵습니다.

(2) GraalVM이 Java의 단점을 보완

  • JVM의 메모리 사용량과 런타임 오버헤드 문제를 해결.
  • Java의 초기 실행 속도 문제를 극복.
  • 네이티브 바이너리로 컴파일되어 배포가 간단.

(3) C/C++ 대비 GraalVM의 장점

특징C/C++Java + GraalVM Native Image
메모리 관리 직접 관리 (malloc/free) 자동 관리 (GC)
보안 취약점 높은 위험 (버퍼 오버플로우 등) 낮은 위험 (포인터 연산 없음)
배포 방식 네이티브 바이너리 네이티브 바이너리
개발 생산성 낮음 높음
초기 실행 속도 빠름 빠름 (JVM 없음)
플랫폼 독립성 플랫폼별 코드 작성 필요 Java 코드로 플랫폼 독립적 작성 후 컴파일 가능

3. 언제 GraalVM이 유리할까?

(1) 서버리스 및 클라우드 환경

  • 서버리스 환경에서는 빠른 시작 시간낮은 메모리 사용량이 중요합니다.
  • GraalVM Native Image로 생성된 Java 애플리케이션은 컨테이너나 서버리스 환경에서 C/C++과 유사한 성능을 제공합니다.

(2) 경량 애플리케이션

  • 리소스가 제한된 IoT 디바이스나 마이크로서비스 환경에서 GraalVM은 C/C++보다 관리 부담이 적고 효율적입니다.

(3) 보안이 중요한 애플리케이션

  • 메모리 안전성이 높은 Java를 사용하면서 네이티브 바이너리를 제공하므로, 보안이 중요한 환경에서 유리합니다.

(4) 기존 Java 프로젝트를 최적화

  • 기존 Java 코드를 GraalVM Native Image로 변환하면, C/C++처럼 높은 성능과 간단한 배포 방식을 동시에 얻을 수 있습니다.

4. 언제 C/C++이 여전히 필요할까?

(1) 하드웨어 제어

  • C/C++은 하드웨어와 밀접하게 동작해야 하는 저수준 작업(예: 드라이버, 펌웨어)에서 여전히 필수적입니다.

(2) 특정 성능 요구사항

  • C/C++은 여전히 Java보다 미세한 메모리 관리최소한의 오버헤드가 필요한 환경에서 유리합니다.

(3) 레거시 코드

  • 기존에 C/C++로 작성된 시스템을 유지보수하거나 확장해야 하는 경우.

5. 결론

Java + GraalVM Native Image는 다음과 같은 경우 C/C++보다 더 나은 선택이 될 수 있습니다:

  1. 개발 생산성안정성을 유지하면서 네이티브 실행 성능이 필요한 경우.
  2. 보안과 메모리 관리 문제가 중요한 경우.
  3. 빠른 배포와 플랫폼 독립성이 필요한 경우.
  4. 서버리스, 클라우드, IoT 같은 경량 애플리케이션 환경에서.

**C/C++**은 여전히 특정 하드웨어 제어나 극한의 성능 요구사항이 필요한 환경에서 유리하지만, Java + GraalVM Native Image는 C/C++의 성능과 배포 방식을 제공하면서도 Java의 안정성과 생산성을 유지할 수 있는 현대적인 대안으로 자리 잡고 있습니다.

결론적으로, GraalVM은 개발자 실수를 줄이고, Java의 장점C/C++의 성능을 모두 제공하는 훌륭한 선택지입니다. C/C++을 꼭 써야 하는 이유가 없다면, GraalVM으로 개발하는 것이 더 나은 경우가 많습니다.

 
4o

나의 말:

근데 그러면 왜 그라브이엠이 디폴트 옵션이 아닌거야? 왜 짓 컴파일러가 돌아가는거지? 음... 그라브이엠을 사용하려면 익스퍼리멘털 뭐 약간 그런 옵션 줘야된다면서 자바 실향할때 왜 그런거지? 그렇게 좋은데?
 

ChatGPT의 말:

ChatGPT

GraalVM이 기본 옵션이 아니라 "실험적(Experimental)" 옵션으로 간주되는 이유는 기술적, 생태계적, 그리고 실용적 이유 때문입니다. GraalVM은 강력하고 혁신적인 기술을 제공하지만, 기존의 JIT(Just-In-Time) 컴파일러와 비교해 아직 넘어야 할 과제들이 있습니다. 아래에서 그 이유를 자세히 설명하겠습니다.


1. GraalVM이 디폴트가 아닌 이유

(1) 기존 JVM 생태계와의 호환성

  • **JIT 컴파일러(C1, C2)**는 수십 년간 Java 생태계의 표준으로 자리 잡아왔습니다.
    • 안정성이 검증되었고, 수많은 애플리케이션에서 사용되고 있습니다.
  • 반면, GraalVM은 비교적 새로운 기술로, JVM의 모든 기능과 완벽히 호환되지 않을 수 있습니다.
    • 특히, 일부 Java 라이브러리와 프레임워크가 동적 클래스 로딩, 리플렉션, 에이전트 기반 도구를 사용하는데, GraalVM Native Image는 이를 제한적으로 지원합니다.

(2) AOT 컴파일의 제한

  • GraalVM Native Image는 동적 기능(예: 리플렉션, 동적 클래스 로딩)을 제한합니다.
    • Java 생태계는 이러한 동적 기능에 크게 의존하기 때문에, 기존 애플리케이션을 GraalVM으로 바로 전환하기 어렵습니다.
  • 개발자가 명시적으로 리플렉션에 필요한 메타데이터를 설정해야 하므로, 설정 복잡도가 증가할 수 있습니다.

(3) 성능 이슈

  • GraalVM은 Native Image로 컴파일된 애플리케이션에서 빠른 시작 속도와 낮은 메모리 사용량을 제공하지만, JIT 컴파일러가 제공하는 런타임 최적화에는 미치지 못할 수 있습니다.
    • JIT는 애플리케이션 실행 중 실시간 데이터를 기반으로 최적화하므로, 장시간 실행되는 애플리케이션(예: 서버)에서는 더 나은 성능을 제공할 수 있습니다.
    • GraalVM Native Image는 AOT 컴파일이므로 런타임 최적화가 제한됩니다.

(4) 빌드 복잡도

  • GraalVM의 Native Image 생성은 일반적인 Java JAR 파일 생성보다 빌드 시간이 오래 걸리고, 추가적인 설정이 필요합니다.
    • 개발자는 런타임 시 필요한 리플렉션, 네이티브 메서드 등을 명시적으로 정의해야 합니다.
    • 이러한 설정은 대규모 프로젝트에서 개발 부담을 증가시킬 수 있습니다.

(5) 안정성과 성숙도

  • GraalVM은 강력한 기술이지만, 기존의 C1/C2 컴파일러만큼의 안정성과 성숙도를 확보했다고 보기 어렵습니다.
    • 예를 들어, 특정 애플리케이션에서 예외적인 동작이 발생할 가능성이 더 높습니다.
    • 이는 Java의 핵심 철학인 **"Write Once, Run Anywhere"**와 충돌할 수 있습니다.

(6) 도구 지원

  • 기존 JVM과 JIT 컴파일러는 다양한 개발 도구(디버거, 프로파일러 등)와 완벽히 통합되어 있습니다.
    • GraalVM은 아직 모든 도구와의 완벽한 통합을 제공하지 못하며, 일부 개발 워크플로우에서 제한적일 수 있습니다.

2. GraalVM의 주요 활용 사례

GraalVM은 여전히 특정 시나리오에서 강력한 장점을 제공합니다. 예를 들어:

(1) 서버리스(Serverless) 환경

  • GraalVM Native Image는 빠른 시작 시간과 낮은 메모리 사용량이 필요한 서버리스 환경에 이상적입니다.

(2) 경량 마이크로서비스

  • Micronaut, Quarkus 같은 프레임워크는 GraalVM Native Image를 활용해 경량 마이크로서비스를 제공합니다.

(3) IoT 및 임베디드 시스템

  • 제한된 리소스를 사용하는 IoT 환경에서 GraalVM의 메모리 최적화는 큰 장점이 됩니다.

(4) 정적 분석 및 언어 통합

  • GraalVM은 Java뿐만 아니라 JavaScript, Python, Ruby 등 여러 언어를 통합적으로 실행할 수 있습니다.

3. 디폴트로 사용되지 않는 이유를 해결하려는 노력

(1) GraalVM의 발전

  • GraalVM은 지속적으로 발전하고 있으며, Java 생태계와의 호환성을 강화하고 있습니다.
  • 예를 들어, Spring Boot, Micronaut, Quarkus 등 주요 Java 프레임워크는 GraalVM Native Image를 지원하기 위해 최적화되고 있습니다.

(2) JIT과의 통합

  • GraalVM은 AOT 컴파일뿐만 아니라, Graal JIT 컴파일러도 제공합니다.
    • Graal JIT는 기존 C2 JIT 컴파일러를 대체하며, JIT 기반 최적화를 지원합니다.
    • 이는 기존 JVM과의 호환성을 유지하면서도 GraalVM의 최적화 이점을 제공합니다.

(3) 개선된 도구 지원

  • GraalVM은 기존 개발 도구와의 통합을 점진적으로 개선하고 있습니다.
  • 특히, 네이티브 이미지 생성과 관련된 설정 복잡도를 줄이기 위해 노력하고 있습니다.

4. 디폴트 옵션이 아닌 상황에 대한 결론

GraalVM은 기존 JIT 컴파일러를 완전히 대체하기보다는 특정 요구사항을 가진 애플리케이션(서버리스, 경량 마이크로서비스 등)에 적합한 선택지로 자리 잡고 있습니다. GraalVM이 디폴트가 아닌 이유는 다음과 같습니다:

  1. Java 생태계와의 완전한 호환성을 아직 확보하지 못함.
  2. AOT 컴파일로 인한 동적 기능 제한과 설정 복잡도.
  3. 장시간 실행되는 애플리케이션에서는 JIT의 런타임 최적화가 유리.
  4. 성숙도와 안정성이 기존 JIT 컴파일러(C1/C2)에 비해 낮음.

그러나 GraalVM은 빠르게 발전하고 있으며, 특정 시나리오에서는 매우 강력한 도구입니다. 미래에는 GraalVM이 더욱 표준화되어 Java의 새로운 디폴트 실행 환경이 될 가능성도 있습니다.