..

JIT 컴파일러란? 인터프리터에서 네이티브 코드까지

TOC


  1. Overview
  2. JIT 컴파일러란?
  3. 왜 JIT 컴파일이 필요한가?
  4. Hot Code 판단 메커니즘
  5. JIT 컴파일과 코드 재사용
  6. Conclusion

Overview


이전 글에서는 JVM의 Execution Engine인터프리터를 통해 바이트코드 명령을 하나씩 읽고 해석해 실행하는 과정을 살펴봤다.

인터프리터 방식은 바이트코드를 바로 실행할 수 있다는 장점이 있지만, 같은 코드가 반복 실행될 때마다 해석 과정이 반복된다. 이번 글에서는 반복 실행되는 코드를 JIT 컴파일러가 어떻게 처리하는지 살펴본다.

JIT 컴파일러란?


JIT(Just-In-Time) 컴파일러는 JVM 실행 중 바이트코드를 CPU가 실행할 수 있는 네이티브 코드로 변환하는 컴파일러다.

JIT 컴파일은 애플리케이션이 시작되기 전에 일괄적으로 수행되지 않는다. JVM이 애플리케이션을 실행하면서 수집한 정보를 바탕으로 필요한 메서드나 코드 구간을 선택해 컴파일한다.

바이트코드
    ↓
인터프리터로 실행
    ↓
Hot Code 판단
    ↓
JIT 컴파일러가 네이티브 코드 생성

왜 JIT 컴파일이 필요한가?


인터프리터는 바이트코드 명령을 실행할 때마다 읽고 해석해야 한다. 같은 메서드가 반복 실행되면 바이트코드 해석이 반복되어 오버헤드가 발생한다.

JIT 컴파일러는 반복 실행되는 코드를 네이티브 코드로 변환한다. 이후에는 같은 메서드를 실행할 때마다 바이트코드를 다시 해석하지 않고, 이미 변환된 코드를 사용할 수 있다.

public int sum(int[] numbers) {
    int result = 0;

    for (int number : numbers) {
        result += number;
    }

    return result;
}

sum 메서드가 반복해서 호출되면 JVM은 실행 정보를 수집하고, 해당 메서드를 JIT 컴파일 대상으로 선택할 수 있다.

Hot Code 판단 메커니즘


JVM의 런타임 프로파일링 기능은 메서드 호출 횟수와 반복문 실행 횟수 같은 정보를 수집한다.

JVM의 컴파일 정책은 수집된 정보와 컴파일 임계값을 바탕으로 자주 실행되는 메서드나 코드 구간을 Hot Code로 판단한다. Hot Code로 판단된 코드는 JIT 컴파일러에 컴파일 작업이 요청된다.

메서드 실행
    ↓
실행 횟수와 실행 정보 수집
    ↓
컴파일 정책이 임계값 확인
    ↓
Hot Code 판단
    ↓
JIT 컴파일 요청

Hot Code 판단 기준과 컴파일 정책의 세부 동작은 JVM 구현과 설정에 따라 달라질 수 있다.

JIT 컴파일과 코드 재사용


JIT 컴파일러는 선택된 바이트코드를 네이티브 코드로 변환하고, JVM은 변환된 코드를 Code Cache에 저장한다.

같은 메서드가 다시 실행되면 JVM은 인터프리터 대신 Code Cache에 저장된 네이티브 코드를 사용해 실행할 수 있다.

인터프리터 실행
    ↓
JIT 컴파일
    ↓
네이티브 코드 생성
    ↓
Code Cache 저장
    ↓
이후 실행에서 재사용

JIT 컴파일에는 시간이 필요하므로, 모든 코드를 컴파일하는 것보다 자주 실행되는 코드를 선택하는 것이 효율적이다.

Conclusion


JVM은 인터프리터로 바이트코드 실행을 시작하고, 런타임 프로파일링과 컴파일 정책을 통해 Hot Code를 판단한다. JIT 컴파일러는 선택된 코드를 네이티브 코드로 변환하고, JVM은 이후 실행에서 변환된 코드를 재사용해 해석 오버헤드를 줄인다.