안드로이드 ART VM의 JIT/AOT 컴파일 최적화 구조 심층 분석

안드로이드 ART VM의 JIT/AOT 컴파일 최적화 구조 심층 분석

안드로이드 ART(Android Runtime)는 기존 Dalvik VM 대비 성능과 효율을 크게 끌어올리기 위해 설계된 런타임 환경이다.
특히 ART는 AOT( Ahead-Of-Time ) 컴파일JIT( Just-In-Time ) 컴파일, 그리고 프로파일 기반 최적화를 결합해 앱 실행 속도와 메모리 사용량, 배터리 효율까지 동시에 고려하는 구조를 가지고 있다.
이 글에서는 ART VM 내부에서 JIT/AOT가 어떻게 협력하는지, 어떤 기준으로 최적화를 수행하는지 심층적으로 정리한다.

1. ART(Android Runtime)의 기본 구조 개요

ART는 자바/코틀린 바이트코드를 직접 해석하는 대신, 이를 **기계어(native 코드)**로 변환해 실행하는 구조를 사용한다.
초기 안드로이드 시대의 Dalvik은 주로 인터프리터 + 간단한 JIT 구조였지만, ART는 다음과 같은 특징을 가진다.

  • dex → native 코드로의 AOT 컴파일 지원
  • 런타임 중 추가 최적화를 위한 JIT 컴파일러 내장
  • 프로파일 정보에 기반한 “필요한 부분만 최적화” 전략
  • 앱 설치/업데이트/실행 시점에 각각 다른 최적화 단계 수행

즉, ART는 정적(AOT) + 동적(JIT)을 혼합한 하이브리드 실행 구조라고 볼 수 있다.

2. AOT(Ahead-Of-Time) 컴파일의 역할과 한계

AOT 컴파일은 앱 설치 또는 시스템 업데이트 시점에 사전에 코드를 기계어로 변환해 두는 방식이다.
이 과정에서 생성된 코드는 /data/dalvik-cache 또는 /data/app 내의 OAT/ART/ODEX 파일 형태로 저장된다.

AOT의 장점

  • 앱 최초 실행 속도 향상(이미 컴파일된 상태)
  • 인터프리터 사용 비율 감소 → CPU 사용량 감소
  • 자주 사용하는 메서드는 미리 최적화된 코드로 실행

하지만 AOT에는 다음과 같은 한계도 있다.

  • 설치 시점에 긴 컴파일 시간 → 설치/업데이트가 오래 걸릴 수 있음
  • 모든 메서드를 무조건 고도 최적화하면 디스크/메모리 사용량 증가
  • 실제로 자주 쓰이지 않는 코드를 최적화하는 “낭비” 발생 가능

그래서 ART는 “모든 것을 AOT로 끝내지 않고” JIT와 프로파일링을 병행하는 구조로 발전했다.

3. JIT(Just-In-Time) 컴파일의 역할과 동작 방식

JIT 컴파일은 앱 실행 중에 실제로 자주 호출되는 메서드를 런타임에서 기계어로 변환하는 방식이다.
ART의 JIT는 단순 속도 향상을 넘어, 다음과 같은 목적을 가진다.

  • 자주 호출되는 코드에만 컴파일 자원 집중
  • 런타임 프로파일을 기반으로 실제 사용 패턴에 최적화
  • AOT로 미리 최적화되지 않은 코드 보완

동작 흐름은 다음과 같다.

  1. 앱 실행 시 대부분의 코드는 인터프리터 또는 간단한 AOT 코드로 실행
  2. VM이 메서드 호출 카운트, 루프 실행 횟수 등 프로파일 데이터를 수집
  3. 임계치를 넘는 “핫(Hot) 코드”를 JIT 컴파일 대상으로 선정
  4. JIT 컴파일러가 백그라운드에서 최적화된 native 코드 생성
  5. 이후 실행부터는 최적화된 JIT 코드 경로로 실행

이 과정에서 JIT는 CPU와 메모리 사용량, 컴파일 비용을 균형 있게 고려해야 한다.

4. 프로파일 기반 최적화(Profile-Guided Optimization)의 핵심

ART 성능 구조의 핵심은 프로파일 기반 최적화다.
단순히 “자주 호출된 메서드”만 보는 것이 아니라, 실제 사용자 사용 패턴을 파일로 저장해 이후 AOT/JIT 결정에 활용한다.

프로파일에 포함되는 대표 정보

  • 자주 호출된 메서드 리스트
  • 자주 접근하는 클래스/필드
  • 인라이닝 가능한 호출 경로 정보
  • 코드 경로별 실행 빈도

이 프로파일 데이터는

  • 앱 실행 중 JIT 최적화 대상 선정
  • 다음 앱 업데이트나 재설치 시 AOT 대상 선정
    에 모두 활용된다.

즉, 한 번 실행한 사용 패턴이 이후 빌드/설치 최적화에도 반영되는 구조라고 볼 수 있다.

5. JIT + AOT를 결합한 계층적(Tiered) 컴파일 구조

ART는 단일 형태의 컴파일 방식이 아니라, 상황에 따라 여러 “레벨”를 두고 코드를 다르게 다룬다.

1단계: 인터프리터/저비용 실행

  • 앱 최초 실행 시 대부분의 코드가 인터프리터 또는 간단한 AOT 코드로 실행
  • 이 단계에서 프로파일 데이터 수집 시작

2단계: JIT 기반 핫 코드 최적화

  • 자주 호출되는 메서드는 JIT로 빠르게 최적화
  • UI 스레드·핵심 로직 등에서 체감 성능 개선

3단계: 프로파일 기반 AOT 컴파일

  • 일정 시간이 지나면, 프로파일을 기반으로 “실제로 많이 쓰인 코드”만 골라 AOT 재컴파일
  • 이후 실행에서는 JIT 부담 감소 + AOT 최적화 코드 활용

이 구조 덕분에 ART는

  • 설치 시간을 줄이면서
  • 실행 중 성능은 최대한 끌어올리고
  • 디스크·메모리 사용량은 억제하는
    방향으로 동작할 수 있다.

6. 앱 시작 속도(Launch Time)에 미치는 영향

ART의 JIT/AOT 구조는 앱 시작 속도에 직접적인 영향을 준다.

AOT가 기여하는 부분

  • 자주 사용하는 클래스 로딩, 초기화 코드가 이미 기계어로 준비
  • 인터프리터 오버헤드 없이 바로 네이티브 코드 실행 가능

JIT가 기여하는 부분

  • 초기 몇 번의 실행 동안 프로파일을 수집
  • 이후 실행에서는 자주 실행되는 초기화 루틴, View 관련 코드, DI/리플렉션 패턴 등도 JIT 최적화 대상이 됨

결과적으로

  • 첫 실행: 다소 느릴 수 있음(프리프로파일 부족)
  • 여러 번 실행 후: 프로파일이 충분히 쌓이면서 체감 속도 개선

이런 이유로 같은 앱이라도 “기기에서 몇 번 실행한 후가 더 빠르게 느껴지는 것”이 ART 구조와 연관이 있다.

7. 메모리 사용량·배터리·발열 측면의 영향

JIT/AOT 최적화는 성능뿐 아니라 자원 사용에도 영향을 준다.

메모리

  • AOT 코드: 디스크 공간 + 메모리 맵핑 영역 필요
  • JIT 코드: 런타임 중 코드 캐시 영역 필요
    → 너무 많은 코드를 JIT/AOT로 최적화하면 코드 캐시/디스크 사용량 증가

배터리·발열

  • JIT 컴파일 자체가 CPU를 사용 → 순간적인 전력 소모
  • 하지만 최적화 이후에는 더 적은 CPU 시간으로 동일 작업 수행 → 장기적으로 절약 가능
  • AOT 비중이 너무 크면 설치 시점 전력·시간 부담이 증가

그래서 ART는 “필요할 때만, 필요한 만큼만” 컴파일하는 전략을 통해 전체 에너지 효율을 높이는 방향으로 설계되어 있다.

8. 개발·튜닝 관점에서 알아둘 포인트

개발자가 ART 내부 동작을 직접 제어할 수 있는 범위는 제한적이지만, 구조를 이해하면 최적화 관점에서 도움이 된다.

  • 작은 메서드, 자주 호출되는 경로 → 인라이닝 및 간결한 로직 설계
  • 불필요한 Reflection·동적 로딩 남발은 JIT/AOT 모두에 부담
  • 앱 시작 시 무거운 초기화 로직 최소화 → 프로파일 수집 이후로 지연
  • 코드 변경이 잦은 모듈은 AOT에 비해 JIT 의존도가 커질 수 있음

결국 ART의 JIT/AOT는 “사용 패턴에 맞춰 알아서 최적화해 주는 시스템”이지만, 코드 구조가 비효율적이면 이 최적화의 효과도 떨어질 수 있다.

9. 정리 및 결론

안드로이드 ART VM은

  • AOT 컴파일로 기본 성능과 실행 효율을 확보하고
  • JIT 컴파일로 실제 사용 패턴에 맞춘 동적 최적화를 수행하며
  • 프로파일 기반 계층적 컴파일 구조를 통해 설치 시간·실행 속도·메모리·배터리를 동시에 고려하는
    하이브리드 런타임 구조를 가지고 있다.

이 구조 덕분에 최신 안드로이드 기기들은
“앱 실행은 빠르면서도, 설치·업데이트 시간과 배터리 소모는 과도하게 증가하지 않는”
균형 잡힌 사용자 경험을 제공할 수 있다.

댓글 남기기