[Java 기본 원리] Java 실행 과정과 컴파일러의 모든 것(AOT vs JIT)

Java 애플리케이션은 소스코드 작성부터 실행까지 컴파일과 실행 엔진을 통한 복잡한 과정을 거친다. 자바는 정적, 동적 컴파일 방식 둘 다 사용한다는게 핵심이다.



Java 실행 과정 💡

  1. 소스코드 컴파일

    1. .java 파일 작성

    2. javac 컴파일러로 컴파일하여 바이트코드(.class) 생성

  2. JVM 실행 단계

    1. 클래스 로더가 바이트코드(.class)를 JVM 런타임 데이터 영역(메모리 영역)에 로드

    2. 실행 엔진을 통한 코드 실행

      • 실행 엔진 방식1: 인터프리터 방식

      • 실행 엔진 방식2: JIT 컴파일러 방식

  3. 실행 엔진 자세히 (심화)

    1. JVM은 먼저, 인터프리터 방식으로 바이트코드(.class) 실행

    2. 실행패턴에 따라 JIT컴파일러로 바이트 코드를 네이티브 코드로 변환하는 최적화
      이걸 왜 하냐?? -> 속도 문제 해결을 위한 JIT컴파일러

      • 인터프리터 방식은 한 라인씩 읽고 실행하므로 반복문의 경우 매번 읽고, 번역 -> 속도 느림

      • 바이트코드(.class) 역시 기계어(0,1,0,1…)로 변환되어야 하기 때문!

      • 따라서 자주 실행되는 바이트 코드 영역을 런타임 중에 기계어로 컴파일하여 사용(캐싱,동적)

      • 즉, Java는 정적 컴파일 방식 동적 컴파일 방식을 함께 사용

    3. JIT컴파일된 네이티브 코드를 최종적으로 함께 실행



컴파일러 비교 - AOT vs JIT 🔍

둘 다 바이트코드(JVM용 언어=.class)를 기계 언어로 변경하는건 동일!
단, 동적이냐 정적이냐 차이이다.

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

이름에서 알 수 있듯이 동적 컴파일 방식이다.

  • 런타임 중 필요한 부분만 컴파일
    • 즉, C나 C++처럼 프로그램을 실행하기 전에 처음 한 번 컴파일하는 대신, 프로그램을 실행하는 시점에서 필요한 부분을 즉석으로 컴파일하는 방식을 말한다.
  • HotSpotVM (JVM 구현중 하나)으로 최적화를 진행!
  • 동적 기능 지원 (리플렉션, JNI)



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

이름에서 알 수 있듯이 정적 컴파일 방식이다.

  • 실행 전 전체 코드 컴파일
    • 즉, C나 C++처럼 프로그램을 실행하기 전에 처음 한 번 컴파일
  • GraalVM (JVM 구현중 하나) 사용
  • AOT는 컴파일 한번이니 당연히 더 빠르지만, 아직까지는 보완할 점이 많다. -> 플랫폼 종속적, 동적기능 부족
    • 런타임에 호출할 수 있는 응용 프로그램의 모든 바이트 코드와 종속성을 미리 알아야 한다. (종속성의 문제)
    • JNI, Java Reflection, 동적 프록시 개체, 클래스 경로 리소스같은 동적기능이 지원되지 않는다.



실무 적용 전략

기본적으로 JIT 컴파일러만 생각하고 개발하면 충분하다. 사실 AOT 방식은 좀 더 발전해야 하기 때문이다.
그래도 함께 활용하고 싶다면 아래 전략을 추천한다.


개발 환경 : JIT 컴파일러 사용

  • 빠른 수정 반영
  • 동적 기능 활용


프로덕션(배포) 환경 : AOT 컴파일러 사용

  • 향상된 런타임 성능
  • 최적화된 실행 속도

댓글남기기