ART: 런타임 및 작동 방식

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

Android Runtime (ART)는 Android 5.0 Lollipop에서 Dalvik을 대체하기 위해 도입된 Android 애플리케이션 런타임 환경입니다. 주요 혁신은 애플리케이션 설치 중에 DEX 바이트코드를 네이티브 머신 코드로 AOT 컴파일하여 JIT 컴파일러 웜업의 오랜 문제를 해결한 것입니다. Google, 2024에 따르면 ART는 DEX 형식과의 완전한 하위 호환성을 유지하면서 Dalvik 대비 20–30%의 성능 향상을 제공합니다.

핵심 사항

  • ART는 AOT 컴파일을 갖춘 Android 런타임으로, Android 5.0에서 Dalvik을 대체했습니다.
  • AOT 컴파일은 앱 설치 중에 DEX 바이트코드를 네이티브 머신 코드로 변환합니다.
  • 하이브리드 JIT+AOT 모드(Android 7.0부터)는 설치를 가속화하고 높은 성능을 유지합니다.
  • ART의 가비지 컬렉션이 개선되어 세대별 수집기 덕분에 일시 중지가 2–3ms로 단축되었습니다.
  • ART는 Dalvik DEX 바이트코드와의 하위 호환성을 유지하고 Java 8+ 기능을 지원합니다.

ART란?

Android Runtime (ART)는 실행 전에 DEX 바이트코드를 네이티브 머신 코드로 컴파일하는 애플리케이션 런타임 환경입니다. 런타임 중에 Just-In-Time 컴파일을 사용했던 Dalvik과 달리 ART는 APK 설치 중에 Ahead-Of-Time(AOT) 컴파일을 수행합니다. 이 근본적인 아키텍처 변경으로 애플리케이션 속도가 크게 향상되고 전력 소비가 줄었습니다.

ART는 처음에 Android 4.4 KitKat에서 실험적 옵션으로 등장했습니다. 개발자는 개발자 설정에서 활성화하여 애플리케이션을 테스트할 수 있었습니다. Android 5.0 Lollipop에서 ART가 기본 런타임이 되었고 Dalvik은 플랫폼에서 완전히 제거되었습니다. Android 7.0 Nougat이 출시될 때까지 ART는 하이브리드 컴파일 모드를 갖추게 되었습니다.

개발 역사

Dalvik을 ART로 교체하기로 한 결정은 갑작스러운 것이 아니었습니다. 새로운 런타임에 대한 작업은 Google이 JIT 접근 방식의 한계를 인식한 2012년에 시작되었습니다. 주요 목표: 앱 시작 가속화, CPU 부하 감소, 전력 소비 절감. 개발은 이전에 Dalvik 최적화 작업을 했던 Android Runtime Group이 주도했습니다.

아키텍처 변경

ART는 Dalvik과 동일한 레지스터 기반 아키텍처를 사용하지만 완전히 재설계된 컴파일러를 갖추고 있습니다. 인터프리터와 JIT 컴파일러 대신 ART에는 설치 중에 DEX 파일을 ELF 바이너리로 변환하는 dex2oat AOT 컴파일러가 포함되어 있습니다. 결과적으로 ART의 애플리케이션은 웜업 단계 없이 즉시 네이티브 성능으로 시작됩니다.

ART 아키텍처: Dalvik에서 새 런타임으로

ART는 Dalvik의 핵심 원칙을 유지했습니다: 별도 프로세스를 통한 애플리케이션 격리, 레지스터 기반 아키텍처, DEX 형식 지원. 그러나 내부 구현은 완전히 다시 작성되었습니다. Dalvik 인터프리터 대신 ART에는 인터프리터, JIT 컴파일러, dex2oat AOT 컴파일러의 세 가지 실행 모드가 포함됩니다. 모드 선택은 애플리케이션의 수명 주기 단계에 따라 다릅니다.

ART의 핵심 구성 요소는 dex2oat(dalvik executable to optimized android translator)입니다. 이 유틸리티는 애플리케이션 설치 중에 실행됩니다(Android 7.0부터는 백그라운드 최적화 중에도 실행). dex2oat는 APK에서 DEX 파일을 읽고, 바이트코드를 최적화하며, OAT 파일(네이티브 코드가 포함된 ELF 바이너리)을 생성합니다. OAT 파일은 /data/dalvik-cache/ 디렉토리에 저장됩니다.

bash
# 기기에서 OAT 파일 확인
adb shell ls -la /data/dalvik-cache/arm64/

# 애플리케이션 강제 재컴파일
adb shell cmd package compile -m speed com.example.app

ART 구성 요소

ART 시스템은 여러 상호 연결된 모듈로 구성됩니다. dex2oat 컴파일러는 네이티브 코드 생성을 담당합니다. 가비지 컬렉터(GC)는 메모리 해제를 관리합니다. 인터프리터는 컴파일 없이 드물게 호출되는 코드를 실행합니다. 프로파일러는 하이브리드 컴파일을 위해 핫 메서드를 추적합니다. 각 모듈은 독립적으로 작동할 수 있어 ART를 유연하고 확장 가능하게 만듭니다.

하이브리드 컴파일: JIT + AOT + 프로파일링

Android 7.0 Nougat부터 ART는 JIT와 AOT의 장점을 결합한 하이브리드 접근 방식을 컴파일에 사용합니다. 애플리케이션 설치 중에 ART는 더 이상 전체 AOT 컴파일을 수행하지 않습니다. 대신 앱은 핫 메서드의 JIT 컴파일과 함께 인터프리트 모드로 실행됩니다. 이렇게 하면 설치 시간과 저장 공간이 줄어듭니다.

백그라운드 프로파일러가 병렬로 작동합니다. 어떤 메서드가 가장 자주 호출되는지, 어떤 코드 분기가 실행되는지, 어떤 클래스가 로드되는지 등의 실행 통계를 수집합니다. 충분한 데이터(일반적으로 2–3회 앱 실행 후)가 축적되면 ART는 백그라운드에서 dex2oat를 실행하고 프로파일된 핫 메서드만 네이티브 코드로 컴파일합니다.

컴파일 모드

ART는 system_server를 통해 관리되는 여러 컴파일 모드를 지원합니다. “speed” 모드는 모든 메서드를 AOT로 컴파일합니다(최대 성능, 느린 설치). “speed-profile” 모드는 프로파일된 핫 메서드만 컴파일합니다(속도와 크기의 균형). “verify” 모드는 컴파일 없이 바이트코드만 확인합니다(최소 공간, 인터프리트). 기본적으로 speed-profile이 사용되며 대부분의 애플리케이션에 최적입니다.

모드컴파일설치 시간성능
speed전체 AOT느림최대
speed-profile프로파일 AOT빠름높음
verify컴파일 없음즉시인터프리트
space최소 AOT중간중간

ART 프로파일러

프로파일러는 특수 .prof 파일에 실행 데이터를 수집합니다. 각 애플리케이션은 /data/misc/profiles/에 프로필을 저장합니다. 임계값(보통 1000개 샘플)에 도달하면 프로파일러가 dex2oat를 실행하여 식별된 핫 메서드를 컴파일합니다. 프로필은 애플리케이션 업데이트 간에 유지되어 OTA 시스템 업데이트 후 재최적화 속도를 높입니다.

ART의 가비지 컬렉션

ART의 가비지 컬렉션은 Dalvik에 비해 획기적으로 개선되었습니다. 단일 스레드 Concurrent Mark and Sweep(CMS) 대신 ART는 여러 최적화 기능을 갖춘 세대별 수집기를 사용합니다: 이동 수집기(힙 압축), 대형 객체 공간(대형 객체를 위한 별도 저장소), 동시 압축(병렬 압축).

ART의 일반적인 GC 일시 중지는 2–3ms로 Dalvik의 5–10ms에 비해 크게 단축되었습니다. 이는 여러 메커니즘을 통해 가능해졌습니다. 첫째, ART는 동시 단계에서 stop-the-world 대신 read-barrier를 사용합니다. 둘째, 세대별 수집기는 대부분의 주기에서 전체 힙을 건드리지 않고 젊은 세대의 객체만 처리합니다. 셋째, 대형 객체 공간(LOS)은 별도로 할당되어 일반 GC 주기에 참여하지 않습니다.

java
// 디버깅을 위한 GC 로그 활성화
System.logV("ART", "GC trigger: allocation failed");

// 강제 GC 호출(프로덕션에서는 권장되지 않음)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    Debug.getRuntimeIStats();
}

ART 시대의 메모리 누수

GC가 개선되었음에도 불구하고 메모리 누수는 여전히 중요한 문제입니다. ART 특정 원인은 적절한 해제 없이 JNI를 통해 네이티브 라이브러리를 로드하는 것입니다. 네이티브 코드가 malloc을 통해 메모리를 할당했지만 free를 호출하지 않으면 ART가 이 메모리를 해제할 수 없습니다. 이 메모리는 관리되는 힙 외부에 있기 때문입니다. Android NDK의 AddressSanitizer 도구는 이러한 누수를 식별하는 데 도움이 됩니다.

ART vs Dalvik: 비교 분석

ART와 Dalvik은 동일한 작업(Android 애플리케이션 실행)에 대한 근본적으로 다른 두 가지 구현입니다. 차이점은 컴파일에서 메모리 관리까지 모든 수준에 영향을 미칩니다. 다음은 주요 성능 및 호환성 매개변수에 대한 비교입니다.

ART의 주요 장점은 JIT 웜업 제거입니다. Dalvik에서는 JIT가 핫 메서드를 컴파일하는 동안 앱이 처음 3–10초 동안 느려질 수 있었습니다. ART에서는 모든 메서드가 이미 네이티브 코드로 컴파일되어 있거나(백그라운드에서 컴파일됨) 있습니다. 이는 특히 게임과 무거운 UI가 있는 앱에서 두드러지며, fps 차이가 ART 쪽으로 15–20%에 달할 수 있습니다.

매개변수DalvikART
컴파일JIT(런타임 중)AOT + 하이브리드(설치 시)
시작 시간3–10초(웜업)즉시
APK 크기~6–7MB(DEX)+20%(OAT)
GC 일시 중지5–10ms2–3ms
전력 소비높음(JIT가 CPU를 가열)낮음(네이티브 코드)

호환성

Dalvik용으로 작성된 모든 애플리케이션은 변경 없이 ART에서 작동합니다. Google은 DEX 바이트코드 수준에서 완전한 하위 호환성을 보장합니다. 예외는 리플렉션을 통해 Dalvik 특정 내부 API를 사용하는 코드입니다: Android SDK에서 @hide로 표시된 dalvik.system.DexFile 클래스의 멤버입니다. 이러한 코드는 공개 API를 사용하도록 업데이트해야 합니다.

Java 8 지원 및 디슈가링

ART는 네이티브 Java 8 기능을 지원하는 최초의 Android 런타임이 되었습니다. Android 7.0부터 ART에는 디슈가링이 포함됩니다. 이는 Java 8 구문(람다, 메서드 참조, Stream API)을 동등한 Java 7 코드로 변환하는 프로세스입니다. 이를 통해 이전 기기와의 호환성을 잃지 않고 최신 구문을 사용할 수 있습니다.

디슈가링은 D8 컴파일러에 의해 수행되며 다음과 같이 작동합니다. 람다가 포함된 소스 코드는 동일한 클래스 내의 합성 메서드로 변환되고 람다는 invoke-custom 호출로 대체됩니다. ART의 런타임에는 Java 8을 위해 특별히 추가된 invoke-custom 명령어에 대한 지원이 포함되어 있습니다. Android 6.0 이하 기기에서는 람다가 익명 클래스로 디슈가링됩니다.

java
// Java 8 람다 — ART에서의 디슈가링
button.setOnClickListener(v -> handleClick(v));

// 디슈가링 후(Java 7에 해당)
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        handleClick(v);
    }
});

디슈가링의 한계

모든 Java 8 기능이 디슈가링에서 지원되는 것은 아닙니다. java.time API(날짜 및 시간)는 build.gradle에 추가되는 추가 라이브러리인 desugar_jdk_libs를 통해서만 사용할 수 있습니다. Stream API도 desugar_jdk_libs가 필요합니다. java.util.functionOptional은 추가 종속성 없이 작동합니다. 완전한 Java 8 지원은 디슈가링 없이 Android 8.0 이상 기기에서 사용할 수 있습니다.

ART를 위한 앱 최적화

ART는 하위 호환되지만 일부 최적화 방법은 특히 이 런타임에서 성능을 향상시킵니다. 주요 권장 사항은 리플렉션을 최소화하는 것입니다. ART는 컴파일 시간에 표시되는 메서드를 직접 머신 코드 호출로 컴파일합니다. 리플렉션은 ART가 추가 스텁을 생성하도록 강제하여 실행 속도를 10–15% 저하시킵니다.

Android 9.0부터 ART는 App Startup Optimization에 대한 지원을 도입했습니다. 개발자는 매니페스트에서 <initialization>을 통해 초기화 클래스를 표시할 수 있으며 ART는 앱 시작 시 이를 사전 로드합니다. 이는 많은 플러그인이나 라이브러리가 있는 애플리케이션의 시작 시간을 5–15% 단축합니다.

xml
<!-- AndroidManifest.xml의 App Startup Optimization -->
<application>
    <profileable
        android:shell="true"
        android:enable="true" />
</application>

성능 테스트

ART에서 성능을 측정하려면 systrace와 perfetto를 사용하십시오. Systrace는 dex2oat 컴파일 시간, GC 빈도 및 프레임 렌더링 속도를 보여줍니다. Perfetto는 더 자세한 정보(스레드 분포, JNI 전환 시간, 네이티브 라이브러리 로드)를 제공합니다. 실행: adb shell perfetto -o /data/misc/perfetto-traces/trace.perfetto -t 10s sched freq idle am wm.

자주 묻는 질문

Android에서 ART란 무엇인가요?

ART(Android Runtime)는 설치 중에 앱 코드를 머신 코드로 컴파일하는 Android 애플리케이션 런타임 환경입니다. 이전 Dalvik 런타임에 비해 앱 시작 및 작동 속도가 빨라집니다.

ART와 Dalvik의 차이점은 무엇인가요?

ART는 애플리케이션 설치 중에 코드를 사전(AOT)에 컴파일하는 반면, Dalvik은 런타임 중에 조각별로(JIT) 컴파일했습니다. 따라서 ART에서 애플리케이션이 더 빠르게 시작되고 전력을 적게 소비합니다.

애플리케이션이 ART에서 실행 중인지 확인하는 방법은?

adb shell getprop을 실행하고 persist.sys.dalvik.vm.lib.2 속성을 찾으세요. 값이 “libart.so”이면 ART, “libdvm.so”이면 Dalvik입니다. Android 5.0+ 기기는 모두 ART를 런타임으로 사용합니다.

ART가 APK 크기에 영향을 미치나요?

최소한으로 영향을 미칩니다. 애플리케이션 자체는 DEX 파일이 포함된 APK 형식으로 유지됩니다. ART는 /data/dalvik-cache/에 추가 OAT 파일을 생성하며, 원본 DEX보다 10–20% 더 많은 공간을 차지하지만 이 저장소는 APK 크기에 포함되지 않습니다.

ART는 Java 8을 지원하나요?

네, ART는 디슈가링 메커니즘을 통해 대부분의 Java 8 기능을 지원합니다. 람다, 메서드 참조 및 함수형 인터페이스는 Android 5.0+의 모든 기기에서 작동합니다. Stream API와 java.time에는 desugar_jdk_libs 라이브러리가 필요합니다.

요약

  • ART는 근본적으로 다른 컴파일 접근 방식으로 Android 5.0 Lollipop에서 Dalvik을 대체한 Android 런타임입니다.
  • AOT 컴파일 dex2oat는 애플리케이션 설치 중에 DEX 바이트코드를 네이티브 ELF 바이너리로 변환합니다.
  • 하이브리드 JIT + AOT 모드(Android 7.0+)는 설치를 가속화하고 실제 사용에 적응합니다.
  • ART의 세대별 가비지 컬렉터는 GC 일시 중지를 5–10ms에서 2–3ms로 줄였습니다.
  • 프로파일러는 2–3회 실행에 걸쳐 데이터를 수집하고 핫 메서드의 백그라운드 컴파일을 트리거합니다.
  • Java 8 디슈가링을 통해 Android 5.0+ 기기에서 람다 및 Stream API를 사용할 수 있습니다.
  • ART에서 최적의 성능을 위해 리플렉션을 최소화하고 App Startup Optimization을 사용하세요.

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

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

프로젝트 논의

더 읽어보기