JIT: Just-In-Time 컴파일이란 무엇이며 어떻게 작동하는가

저자: IT Sectr 게시일: 2026-04-16 읽는 시간: 9 분

JIT(Just-In-Time)은 바이트코드 또는 프로그램의 중간 표현을 실행 중에 직접 기계 명령어로 변환하는 동적 컴파일 기술입니다. Android에서 JIT 컴파일러는 Dalvik 가상 머신의 일부로 버전 2.2 Froyo에 처음 등장하여 애플리케이션 실행 속도를 2–5배 향상시켰습니다. Google, 2024에 따르면, ART의 최신 JIT는 인터프리터 실행과 핫 메서드의 프로파일 기반 컴파일을 결합합니다.

핵심 요약

  • JIT — Just-In-Time 컴파일: 프로그램 실행 중에 코드를 직접 기계어로 변환합니다.
  • Dalvik에서 JIT는 호출 임계값(약 200회)을 초과한 핫 메서드를 컴파일했습니다.
  • JIT는 설치 시간을 줄이고 전체 AOT 컴파일보다 적은 공간을 차지합니다.
  • 주요 단점은 워밍업 지연입니다. 처음 몇 초 동안 애플리케이션이 더 느리게 실행됩니다.
  • 최신 ART에서 JIT는 백그라운드 AOT 최적화와 함께 하이브리드 모드로 사용됩니다.

JIT 컴파일이란?

Just-In-Time(JIT)은 소스 코드나 바이트코드를 미리(AOT처럼) 변환하지 않고, 프로그램의 해당 섹션이 처음 호출될 때 기계 명령어로 변환하는 컴파일 방법입니다. “Just-In-Time”이라는 용어는 컴파일이 실행 직전에 “딱 맞춰” 발생함을 의미합니다.

JIT의 개념은 1960년대부터 존재했지만, 1995년 Java 가상 머신의 등장과 함께 널리 채택되었습니다. JIT는 바이트코드의 이식성(한 번 작성하면 어디서나 실행)과 네이티브 코드에 가까운 성능을 결합할 수 있게 합니다. Java HotSpot VM에서 JIT 컴파일러는 실행 코드를 분석하고 가장 중요한 섹션만 컴파일하여 시간과 메모리를 절약합니다.

작동 원리

JIT 컴파일러는 입력으로 바이트코드를 받아 해석하고 동시에 통계를 수집합니다. 코드의 특정 섹션(메서드, 루프)이 충분히 자주 호출되면 JIT는 이를 컴파일하기로 결정합니다. 컴파일된 기계 코드는 캐시에 저장되어 이후 호출 시 이미 컴파일된 버전이 사용됩니다. 이는 전체 프로그램을 컴파일할 필요 없이 가속화를 제공합니다.

java
// 예: 여러 번 호출된 후 메서드가 핫이 됩니다
public class HotMethod {
    private int compute(int n) {
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += i * i;
        }
        return sum;
    }
}

// 루프에서 500회 호출 — JIT가 compute를 컴파일합니다
for (int t = 0; t < 500; t++) {
    hot.compute(1000);
}

Android의 JIT: Dalvik과 ART

Android에서 JIT 컴파일은 세 단계의 진화를 거쳤습니다. 첫 번째 단계 — JIT 없는 Dalvik(Android 1.0–2.1): DEX 바이트코드의 순수 해석. 두 번째 단계 — JIT 있는 Dalvik(Android 2.2–4.4): JIT 컴파일러 도입으로 애플리케이션 속도 2–5배 향상. 세 번째 단계 — 하이브리드 JIT의 ART(Android 7.0+): 새로운 기능으로 JIT의 귀환.

Dalvik의 JIT는 추적 기반 컴파일러로 구현되었습니다. 개별 메서드가 아니라 자주 순차적으로 실행되는 명령어 체인(추적)을 분석했습니다. 이는 여러 메서드를 포함한 전체 실행 경로를 컴파일할 수 있게 했습니다. 이 접근 방식은 명령어 캐시가 작은 모바일 프로세서에 효과적이었으며, 컴파일된 추적이 L1 캐시에 들어맞았습니다.

최신 ART의 JIT

Android 7.0 Nougat부터 ART는 메서드 기반 JIT를 사용합니다. 실행 프로필을 기반으로 개별 메서드를 컴파일합니다. 이 JIT는 Dalvik JIT보다 훨씬 빠르게 작동합니다. 한 메서드의 일반적인 컴파일 시간은 0.5–1ms로 Dalvik의 3–5ms와 비교됩니다. 컴파일된 코드는 애플리케이션 힙 대신 별도의 메모리 영역(JIT 코드 캐시)에 저장되어 단편화를 줄입니다.

매개변수Dalvik JITART JIT
유형추적 기반메서드 기반
컴파일 속도3–5ms/메서드0.5–1ms/메서드
컴파일 임계값약 200회 호출동적
코드 캐시애플리케이션 힙 내JIT 코드 캐시
프로파일링내부외부 .prof 파일

핫 메서드 감지 및 컴파일 임계값

JIT의 핵심 메커니즘은 핫 메서드 감지입니다. 각 메서드 호출은 내부 카운터를 증가시킵니다. 카운터가 임계값을 초과하면 메서드는 “핫”으로 표시되고 컴파일로 전송됩니다. Dalvik에서는 임계값이 고정되어 있었습니다(약 200회 호출). ART에서는 카운터가 장치의 사용 가능한 리소스에 따라 동적으로 구성됩니다.

컴파일 프로세스는 여러 단계를 포함합니다. 첫 번째 — 바이트코드 분석: JIT가 명령어 스트림을 검사하고 데이터 흐름 그래프를 구축합니다. 두 번째 — 최적화: 작은 메서드의 인라인화, 죽은 코드 제거, 상수 접기. 세 번째 — 코드 생성: 최적화된 그래프를 특정 CPU 아키텍처(ARM, ARM64, x86)의 기계 명령어로 변환합니다.

java
// 인라이닝 데모 — JIT가 메서드 본문을 인라인화합니다
public int inlineExample() {
    return square(5);
}

private int square(int x) {
    return x * x;
} // JIT가 호출을 return 5 * 5;로 대체합니다

OSR — On-Stack Replacement

JIT의 특별한 기술 — On-Stack Replacement(OSR). 메서드에 수백 번의 반복 동안 종료되지 않는 긴 루프가 포함된 경우, JIT는 루프를 “즉시” 컴파일하고 실행 중에 해석된 버전을 컴파일된 버전으로 대체할 수 있습니다. OSR은 렌더링, 이미지 처리, 암호화 같은 계산 집약적 작업에 특히 효과적입니다.

JIT vs AOT: 비교 분석

JIT와 AOT는 상반된 트레이드오프를 가진 두 가지 컴파일 접근 방식입니다. JIT는 컴팩트한 배포 크기와 적응성을 위해 첫 실행 속도를 희생합니다. AOT는 최초부터 최대 성능을 위해 설치 시간과 디스크 공간을 희생합니다. 어느 접근 방식이 절대적으로 더 낫다고 할 수 없으며, 선택은 시나리오에 따라 달라집니다.

JIT의 주요 장점은 적응형 최적화입니다. JIT는 AOT에서 사용할 수 없는 프로필 정보(정확한 객체 유형, 실제 호출 빈도, 실제 분기 패턴)를 활용할 수 있습니다. 이를 통해 정적 컴파일로는 불가능한 적극적인 최적화를 적용할 수 있습니다. 예를 들어, 실제로 하나의 수신자 유형만 발견되는 경우 JIT는 메서드 호출을 비가상화할 수 있습니다.

기준JITAOT
설치 시간즉시크기에 따라 다름
첫 실행느림(워밍업)빠름
디스크 공간최소+15–30%
적응성높음낮음
CPU 사용량컴파일 시 급증안정적

JIT를 선택해야 하는 경우

JIT 컴파일은 빠른 배포와 디스크 공간 절약이 중요할 때 선호됩니다. 모바일 개발 맥락에서 JIT는 자주 업데이트되는 애플리케이션(A/B 테스트, 핫픽스)에 이상적입니다. JIT는 개발 중에도 편리하며, 코드가 하루에 수십 번 재구축될 때 컴파일에서 절약된 모든 초가 피드백 루프를 가속화합니다.

JIT 컴파일의 장점

JIT는 개발자에게 여러 실용적인 이점을 제공합니다. 첫째 — 작은 APK 크기. JIT 접근 방식에서는 APK에 바이트코드(DEX)만 패키징되어 컴파일된 네이티브 코드보다 20–30% 적은 공간을 차지합니다. 내장 저장 공간이 제한된 사용자에게 이는 중요한 이점입니다.

두 번째 장점은 장치 적응입니다. JIT는 실제 CPU 아키텍처, RAM 용량 및 현재 부하를 고려하여 코드를 컴파일합니다. 예를 들어, 2GB RAM 장치에서는 JIT가 메모리 절약을 위해 덜 적극적으로 컴파일하고, 12GB 플래그십에서는 가능한 모든 최적화를 적용할 수 있습니다. 반면 AOT 컴파일은 설치 시점에 결정을 고정합니다.

플랫폼 독립성

바이트코드는 플랫폼에 독립적으로 유지되어 애플리케이션 배포를 단순화합니다. 하나의 APK가 ARM, ARM64, x86 장치에서 작동하고 JIT가 각 아키텍처에 대한 네이티브 코드를 생성합니다. AOT 접근 방식은 APK에 여러 네이티브 코드 변형을 포함(크기 증가)하거나 각 아키텍처에 대해 별도 버전을 컴파일해야 합니다.

JIT의 단점과 한계

JIT의 주요 단점은 워밍업 지연입니다. 사용자는 JIT가 핫 메서드를 컴파일하는 동안 애플리케이션의 첫 몇 초 동안 느려짐을 경험합니다. 게임에서는 초기 레벨에서 끊김 현상으로 나타납니다. 애니메이션이 있는 애플리케이션에서는 화면 간 첫 전환 시 버벅거림이 발생합니다.

두 번째 단점 — 전력 소비. 컴파일 프로세스는 CPU에 큰 부하를 주어 워밍업 기간 동안 전력 소비를 10–20% 증가시킵니다. 배터리로 작동하는 장치에서는 배터리 수명이 단축됩니다. 이는 애플리케이션 재시작이 빈번한 시나리오(시스템이 프로세스를 언로드하고 다시 로드하는 제한된 메모리의 멀티태스킹)에서 특히 두드러집니다.

캐시 단편화

또 다른 문제 — JIT 캐시 단편화. 컴파일된 코드는 연속된 메모리 영역에 저장됩니다. 새 클래스가 로드되고 추가 메서드가 컴파일되면 캐시가 단편화되어 메모리 관리 오버헤드가 증가합니다. Dalvik에서는 정기적인 캐시 정리로 이 문제를 해결했습니다. ART에서는 JIT 캐시가 힙과 별도로 할당되며 자체 조각 모음 전략을 사용합니다.

하이브리드 모드: 두 세계의 장점

ART의 현대적 접근 방식 — 하이브리드 컴파일은 JIT와 AOT의 강점을 결합합니다. 애플리케이션 설치 중에는 컴파일이 수행되지 않고 바이트코드 검증(verify)만 수행됩니다. 이는 빠른 설치와 최소한의 공간 사용을 보장합니다. 첫 번째 실행은 핫 메서드의 JIT 컴파일과 함께 인터프리터 모드로 실행되어 사용자가 긴 대기 시간 없이 수용 가능한 성능을 얻을 수 있습니다.

동시에 백그라운드 프로파일러가 실제 사용 데이터를 수집합니다. 2–3회의 완전한 애플리케이션 실행 후 프로필이 충분한 완성도에 도달하면 시스템이 dex2oat를 실행하여 핫 메서드를 네이티브 코드로 컴파일합니다. 이 작업은 장치에 부하가 없을 때(충전 중, 화면 꺼짐) 백그라운드에서 수행됩니다. 백그라운드 AOT가 완료되면 애플리케이션은 완전한 AOT 컴파일에 필적하는 성능을 달성합니다.

bash
# 백그라운드 컴파일 강제 시작
adb shell cmd package compile -m speed-profile -f com.example.app

# 컴파일 상태 보기
adb shell cmd package dump-profiles com.example.app

하이브리드 접근 방식의 결과

Google I/O 2017에 따르면, 하이브리드 컴파일은 순수 AOT에 비해 애플리케이션 설치 시간을 30–50% 단축했습니다. 시스템 파티션의 점유 디스크 공간은 20–30% 감소했습니다. 동시에 백그라운드 컴파일 후 성능은 완전한 AOT 수준과 일치합니다. 하이브리드가 AOT보다 떨어지는 유일한 시나리오는 설치 직후 첫 실행입니다. 애플리케이션이 JIT 모드로 실행되어 10–15% 더 느릴 수 있습니다.

자주 묻는 질문

간단히 말해 JIT 컴파일이란 무엇인가요?

JIT는 코드를 미리 번역하지 않고 실행 중에 부분적으로 기계어로 변환하여 프로그램을 가속화하는 방법입니다. 가장 빈번한 부분은 컴파일되어 캐시되고, 드문 부분은 원래 형태로 유지됩니다.

JIT와 AOT의 차이점은 무엇인가요?

JIT는 실행 중에 코드를 컴파일하여 공간을 절약하고 설치를 빠르게 합니다. AOT는 모든 코드를 미리 컴파일하여 애플리케이션 시작은 빠르지만 더 많은 디스크 공간과 설치 시간이 필요합니다.

왜 JIT가 Android에서 제거되었나요?

JIT는 제거된 것이 아니라 진화했습니다. Android 5.0에서는 JIT가 있는 Dalvik이 순수 AOT를 사용하는 ART로 대체되었습니다. Android 7.0에서는 JIT가 하이브리드 시스템의 일부로 ART에 돌아와 최적의 성능을 위해 백그라운드 AOT 컴파일과 함께 작동합니다.

JIT는 전력 소비에 어떤 영향을 미치나요?

JIT는 CPU 부하로 인해 워밍업 기간 동안 전력 소비를 10–20% 증가시킵니다. 핫 메서드 컴파일이 완료되면 전력 소비는 정상 수준으로 돌아갑니다. ART의 하이브리드 모드는 백그라운드 컴파일을 통해 이러한 급증을 최소화합니다.

JIT 워밍업이 사용자에게 보이나요?

네, 집중적인 계산 시나리오에서 그렇습니다. 사용자는 애플리케이션 작동의 첫 몇 초 또는 게임 시작 시 느려짐을 인지할 수 있습니다. 최신 Android 버전(8.0+)에서는 하이브리드 모드가 프로파일 기반 컴파일 덕분에 이 효과를 최소화합니다.

요약

  • JIT(Just-In-Time)는 실행 중에 바이트코드를 기계 명령어로 변환하는 동적 컴파일입니다.
  • Android에서 JIT는 진화했습니다: Dalvik의 추적 기반 → 완전한 AOT → 최신 ART의 하이브리드 JIT+AOT.
  • 핫 메서드는 호출 카운터를 통해 감지되며 임계값(약 200회 호출) 초과 시 컴파일됩니다.
  • OSR(On-Stack Replacement)을 통해 실행을 중단하지 않고 긴 루프를 즉시 컴파일할 수 있습니다.
  • JIT의 주요 장점: 작은 APK 크기, 빠른 설치 및 장치 적응.
  • 주요 단점: 워밍업 지연, 최고 전력 소비 및 캐시 단편화.
  • ART 하이브리드 모드(Android 7.0+)는 높은 성능을 유지하면서 설치 시간을 30–50% 단축합니다.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기