ART 구성

이 페이지에서는 ART 및 컴파일 옵션을 구성하는 방법을 설명합니다. 또한 시스템 이미지의 사전 컴파일 구성 및 dex2oat 컴파일 옵션의 주제를 비롯하여 시스템 파티션 공간, 데이터 파티션 공간, 성능 사이에서 균형을 유지하는 방법도 설명합니다.

ART 사용 방법은 source.android.com의 ART 및 Dalvik 페이지, Dalvik Executable 형식 페이지 및 나머지 페이지를 참조하세요. 앱이 제대로 작동하는지 확인하려면 Android 런타임(ART)에서 앱 동작 확인을 참조하세요.

ART 작동 방식

ART는 AOT(ahead-of-time) 컴파일을 사용하며 Android 7.0(Nougat 또는 N)부터는 AOT, JIT(just-in-time) 컴파일, 프로필 기반 컴파일을 조합하여 사용합니다. 이러한 모든 컴파일 모드의 조합은 구성 가능하며 이 섹션에서 설명합니다. 예를 들어 Pixel 기기는 다음의 컴파일 흐름으로 구성됩니다.

  1. 애플리케이션은 처음에 AOT 컴파일 없이 설치됩니다. 애플리케이션이 처음 몇 번 실행되면 애플리케이션이 인식되고 자주 실행되는 메서드가 JIT 컴파일됩니다.
  2. 기기가 유휴 상태에서 충전 중이면 컴파일 데몬이 실행되어 처음 실행 시 생성된 프로필을 기반으로 자주 사용되는 코드를 AOT 컴파일합니다.
  3. 다음번에 애플리케이션이 다시 시작되면 프로필 기반 코드가 사용되고 런타임 시 이미 컴파일된 메서드에 JIT 컴파일이 실행되지 않습니다. 새 실행 시 JIT 컴파일된 메서드가 프로필에 추가되고 이후에 컴파일 데몬에 의해 선택됩니다.

ART는 컴파일러(dex2oat 도구), 그리고 Zygote 시작을 위해 로드되는 런타임(libart.so)으로 구성됩니다. dex2oat 도구는 APK 파일을 사용하고 런타임 시 로드되는 컴파일 아티팩트 파일을 하나 이상 생성합니다. 파일 수, 확장자 및 이름은 출시에 따라 다를 수 있지만 Android O 출시에서는 다음과 같은 파일이 생성됩니다.

  • .vdex: APK의 압축되지 않은 DEX 코드가 들어 있고, 확인 속도를 높이기 위한 추가 메타데이터가 들어 있습니다.
  • .odex: APK의 메서드에 사용되는 AOT 컴파일된 코드가 들어 있습니다.
  • .art (optional): APK에 나열된 일부 문자열과 클래스의 ART 내부 표현이 포함되어 있습니다. 이러한 항목은 애플리케이션 시작 속도를 높이는 데 사용됩니다.

컴파일 옵션

ART의 컴파일 옵션은 다음 두 가지 카테고리로 나뉩니다.

  1. 시스템 ROM 구성: 시스템 이미지를 빌드할 때 AOT 컴파일되는 코드를 나타냅니다.
  2. 런타임 구성: ART가 기기에서 애플리케이션을 컴파일하고 실행하는 방법을 나타냅니다.

이 두 카테고리를 구성하는 한 가지 핵심 ART 옵션은 컴파일러 필터입니다. 컴파일러 필터는 dex2oat 도구에 전달되는 옵션으로, ART가 DEX 코드를 컴파일하는 방법을 제어합니다. Android O부터 다음의 네 가지 필터가 공식 지원됩니다.

  • verify: DEX 코드 확인만 실행합니다.
  • quicken: DEX 코드 확인을 실행하고, 인터프리터 성능 향상을 위해 일부 DEX 명령어를 최적화합니다.
  • speed: DEX 코드 확인을 실행하고 모든 메서드를 AOT 컴파일합니다.
  • speed-profile: DEX 코드 확인을 실행하고 프로필 파일에 나열된 메서드를 AOT 컴파일합니다.

시스템 ROM 구성

시스템 ROM을 구성하는 데 사용할 수 있는 ART 빌드 옵션에는 여러 가지가 있습니다. 이러한 옵션을 구성하는 방법은 /system에 사용 가능한 저장공간과 사전 설치된 애플리케이션 수에 따라 다릅니다. 시스템 ROM으로 컴파일되는 JAR/APK는 다음과 같은 네 가지 카테고리로 나눌 수 있습니다.

  • 부트 클래스 경로 코드: 기본적으로 speed 컴파일러 필터로 컴파일됩니다.
  • 시스템 서버 코드: 기본적으로 speed 컴파일러 필터로 컴파일됩니다.
  • 제품별 핵심 애플리케이션: 기본적으로 speed 컴파일러 필터로 컴파일됩니다.
  • 그 외 모든 애플리케이션: 기본적으로 quicken 컴파일러 필터로 컴파일됩니다.

Makefile 옵션

  • WITH_DEXPREOPT
  • 시스템 이미지에 설치된 DEX 코드에서 dex2oat를 호출할지 여부입니다. 기본적으로 사용 설정됩니다.

  • DONT_DEXPREOPT_PREBUILTS(Android L부터)
  • DONT_DEXPREOPT_PREBUILTS를 사용 설정하면 사전 빌드가 미리 최적화되지 않습니다. Gmail과 같이 Android.mkinclude $(BUILD_PREBUILT)가 지정된 앱이 이에 해당합니다. Google Play를 통해 업데이트될 가능성이 있는 사전 빌드된 앱의 사전 최적화 작업을 건너뛰면 /system 공간이 절약되지만 첫 부팅 시간이 늘어납니다.

  • PRODUCT_DEX_PREOPT_DEFAULT_COMPILER_FILTER(Android 9부터)
  • PRODUCT_DEX_PREOPT_DEFAULT_COMPILER_FILTER는 사전 최적화된 애플리케이션의 기본 컴파일러 필터를 지정합니다. Gmail과 같이 Android.mkinclude $(BUILD_PREBUILT)가 지정된 앱이 이에 해당합니다. 지정되지 않은 경우 기본값은 quicken입니다.

  • WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY(Android O MR1의 새로운 기능)
  • WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY을 사용 설정하면 부트 클래스 경로와 시스템 서버 jar만 사전 최적화됩니다.

  • LOCAL_DEX_PREOPT
  • 모듈 정의에서 LOCAL_DEX_PREOPT 옵션을 지정하여 사전 최적화를 앱별로 사용 설정하거나 중지할 수도 있습니다. Google Play 업데이트에 따라 시스템 이미지의 사전 최적화된 코드가 더 이상 사용되지 않기 때문에 이 기능은 Google Play 업데이트를 즉시 받을 수 있는 앱의 사전 최적화를 사용 중지할 때 유용합니다. 또한 이 기능은 사용자가 데이터 파티션에 최신 버전의 앱을 이미 설치했을 수 있기 때문에 주요 버전 업그레이드 OTA의 공간을 절약하는 데에도 도움이 됩니다.

    LOCAL_DEX_PREOPT는 사전 최적화를 사용 설정 또는 중지하도록 'true' 또는 'false' 값을 각각 지원합니다. 또한 사전 최적화를 통해 APK 또는 JAR 파일에서 classes.dex 파일이 삭제되어서는 안 되는 경우 'nostripping'을 지정할 수 있습니다. 일반적으로 이 파일은 사전 최적화 후에 더 이상 필요하지 않기 때문에 삭제되지만, 이 마지막 옵션은 타사 APK 서명을 유효한 상태로 유지하는 데 필요합니다.

  • PRODUCT_DEX_PREOPT_BOOT_FLAGS
  • 부팅 이미지가 컴파일되는 방식을 제어하는 옵션을 dex2oat에 전달합니다. 맞춤설정된 이미지 클래스 목록과 컴파일된 클래스 목록, 컴파일러 필터를 지정하는 데에도 사용할 수 있습니다.

  • PRODUCT_DEX_PREOPT_DEFAULT_FLAGS
  • 부팅 이미지 외의 모든 항목을 컴파일하는 방법을 제어하는 옵션을 dex2oat에 전달합니다.

  • PRODUCT_DEX_PREOPT_MODULE_CONFIGS
  • 특정 모듈 및 제품 구성을 위한 dex2oat 옵션을 전달하는 기능을 제공합니다. 이 옵션은 $(call add-product-dex-preopt-module-config,<modules>,<option>)에 의해 제품의 device.mk 파일에 설정됩니다. 여기서 <modules>는 각각 JAR 파일과 APK 파일의 LOCAL_MODULE 이름 및 LOCAL_PACKAGE 이름 목록입니다.

  • PRODUCT_DEXPREOPT_SPEED_APPS (New in Android O)
  • 제품의 핵심으로 확인된 애플리케이션 목록으로, speed 컴파일러 필터로 컴파일하는 것이 적합합니다. 예를 들어 SystemUI와 같은 영구 앱은 다음번 재부팅 시에만 프로필 기반 컴파일을 사용할 가능성이 있으므로, 제품에서 이러한 앱은 항상 AOT로 컴파일하는 것이 좋습니다.

  • PRODUCT_SYSTEM_SERVER_APPS (New in Android O)
  • 시스템 서버에 의해 로드되는 애플리케이션 목록. 이러한 애플리케이션은 기본적으로 speed 컴파일러 필터로 컴파일됩니다.

  • PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD(Post Android O)
  • 기기에 ART의 디버그 버전을 포함할지 여부. 기본적으로 이 디버그 버전은 userdebug 빌드와 eng 빌드에 사용됩니다. 이 동작은 옵션을 true 또는 false로 명시적으로 설정하여 재정의할 수 있습니다.

    기본적으로 기기는 디버그 버전이 아닌 버전(libart.so)을 사용합니다. 전환하려면 시스템 속성 persist.sys.dalvik.vm.lib.2libartd.so로 설정합니다.

  • WITH_DEXPREOPT_PIC (Removed in Android O)
  • Android 5.1.0~Android 6.0.1에서는 PIC(위치 비종속 코드)를 사용하도록 WITH_DEXPREOPT_PIC를 지정할 수 있습니다. 그렇게 지정하면 이미지에서 컴파일된 코드를 /system에서 /data/dalvik-cache로 재배치할 필요가 없기 때문에 데이터 파티션 공간을 절약할 수 있습니다. 그러나 위치 종속 코드를 활용하는 최적화가 사용되지 않기 때문에 런타임에 약간의 영향이 있습니다. 일반적으로 /data의 공간을 절약하고자 하는 기기는 PIC 컴파일을 사용 설정해야 합니다.

    Android 7.0에서는 PIC 컴파일이 기본적으로 사용 설정되었습니다.

  • WITH_DEXPREOPT_BOOT_IMG_ONLY(Android O MR1에서 삭제됨)
  • 이 옵션은 시스템 서버 jar도 사전 선택하는 WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY로 대체되었습니다.

부트 클래스 경로 구성

  • 미리 로드된 클래스 목록
  • 미리 로드된 클래스 목록은 시작 시 zygote가 초기화하는 클래스 목록입니다. 이 목록을 통해 각 앱이 클래스 초기화 프로그램을 별도로 실행하지 않아도 되므로, 앱을 더 빠르게 시작하고 메모리에서 페이지를 공유할 수 있습니다. 미리 로드된 클래스 목록 파일은 기본적으로 frameworks/base/config/preloaded-classes에 있으며 일반적인 스마트폰 사용에 맞게 조정된 목록을 포함하고 있습니다. 웨어러블 기기와 같은 다른 기기에서는 이 목록이 다를 수 있으므로 기기에 맞게 조정해야 합니다. 이 목록을 조정할 때는 주의가 필요합니다. 클래스를 너무 많이 추가하면 사용되지 않는 클래스가 로드될 때 메모리가 낭비됩니다. 또한, 클래스를 너무 적게 추가해도 각 앱이 자체 사본을 보유해야 하므로 마찬가지로 메모리가 낭비됩니다.

    사용 예(제품의 device.mk에서):

    PRODUCT_COPY_FILES += <filename>:system/etc/preloaded-classes
    

    참고: 이 행은 build/target/product/base.mk로부터 기본값을 얻는 모든 제품 구성 makefile을 상속하기 전에 배치해야 합니다.

  • 이미지 클래스 목록
  • 이미지 클래스 목록은 dex2oat가 미리 초기화하여 boot.art 파일에 저장하는 클래스 목록입니다. 이 목록을 통해 zygote는 미리 로드 시 이러한 클래스 자체를 위한 초기화 프로그램을 실행하는 대신, 시작 시 boot.art 파일에서 결과를 로드할 수 있습니다. 이것의 핵심 기능은 이미지에서 로드되고 프로세스 간에 공유되는 페이지가 정리될 수 있기 때문에 메모리가 부족한 상태에서도 페이지가 쉽게 교환될 수 있다는 점입니다. L의 경우 기본적으로 이미지 클래스 목록은 미리 로드된 클래스 목록과 동일한 목록을 사용합니다. AOSP의 L 이후부터 맞춤설정 이미지 클래스 목록은 다음을 사용하여 지정할 수 있습니다.

    PRODUCT_DEX_PREOPT_BOOT_FLAGS
    

    사용 예(제품의 device.mk에서):

    PRODUCT_DEX_PREOPT_BOOT_FLAGS += --image-classes=<filename>
    
  • 컴파일된 클래스 목록
  • AOSP의 L 이후부터 부트 클래스 경로의 클래스 하위 집합은 컴파일된 클래스 목록을 사용하여 사전 최적화 중에 컴파일되도록 지정할 수 있습니다. 이 옵션은 공간이 매우 부족하고 사전 최적화된 전체 부팅 이미지에 맞지 않는 기기에 유용합니다. 그러나 이 목록에 지정되지 않은 클래스는 기기에서도 컴파일되지 않기 때문에 반드시 해석되어야 한다는 점을 기억하세요. 그러지 않으면 런타임 성능에 영향을 미칠 수 있습니다. 기본적으로 dex2oat는 $OUT/system/etc/compiled-classes에서 컴파일된 클래스 목록을 찾기 때문에, device.mk가 맞춤설정된 클래스 목록을 그 위치에 복사할 수 있습니다. 특정 파일 위치는 다음을 사용하여 지정할 수도 있습니다.

    PRODUCT_DEX_PREOPT_BOOT_FLAGS
    

    사용 예(제품의 device.mk에서):

    PRODUCT_COPY_FILES += <filename>:system/etc/compiled-classes
    

    참고: 이 행은 build/target/product/base.mk로부터 기본값을 얻는 모든 제품 구성 makefile을 상속하기 전에 배치해야 합니다.

런타임 구성

JIT 옵션

다음 옵션은 ART JIT 컴파일러를 사용할 수 있는 Android 출시에만 해당합니다.

  • dalvik.vm.usejit: JIT의 사용 설정 여부
  • dalvik.vm.jitinitialsize(기본값 64K): 코드 캐시의 초기 용량. 코드 캐시는 규칙적으로 GC되고 필요한 경우 증가됩니다.
  • dalvik.vm.jitmaxsize(기본값 64M): 코드 캐시의 최대 용량.
  • dalvik.vm.jitthreshold: (기본값 10000) - 메서드의 JIT 컴파일을 위해 메서드의 'hotness' 카운터가 전달해야 하는 임계값입니다. 'hotness' 카운터는 런타임 내부의 측정항목입니다. 여기에는 호출 수, 이전 분기 및 기타 요소가 포함됩니다.
  • dalvik.vm.usejitprofiles: JIT 프로필의 사용 여부. dalvik.vm.usejit가 false인 경우에도 사용할 수 있습니다. dalvik.vm.usejit가 false인 경우 컴파일러 필터 speed-profile은 어떤 메서드도 AOT 컴파일하지 않으며 quicken과 동일합니다.
  • dalvik.vm.jitprithreadweight(기본값은 dalvik.vm.jitthreshold/20로 설정됨) - 애플리케이션 UI 스레드와 관련된 JIT '샘플'의 가중치(jitthreshold 참조). 앱과 상호작용할 때 사용자 환경에 직접적인 영향을 주는 메서드를 빠르게 컴파일할 때 사용합니다.
  • dalvik.vm.jittransitionweight: (기본값은 dalvik.vm.jitthreshold/10으로 설정됨) 컴파일 코드와 인터프리터 간을 전환하는 메서드 호출의 가중치. 이 옵션은 전환(비용이 많이 소요됨)을 최소화하도록 관련 메서드를 컴파일하는 데 도움이 됩니다.

패키지 관리자 옵션

Android 7.0부터 다양한 단계에서 발생한 컴파일/확인의 수준을 지정할 수 있는 일반적인 방법이 있습니다. 컴파일 수준은 시스템 속성을 통해 구성할 수 있으며 기본값은 다음과 같습니다.

  • pm.dexopt.install=speed-profile
  • Google Play를 통해 애플리케이션을 설치할 때 사용되는 컴파일 필터입니다. dex 메타데이터 파일의 프로필을 사용할 수 있도록 설치 필터를 speed-profile로 설정하는 것이 좋습니다. 프로필이 제공되지 않거나 비어 있는 경우 speed-profile은 quicken과 동일합니다.

  • pm.dexopt.bg-dexopt=speed-profile
  • 기기가 유휴 상태에서 충전 중이거나 완전히 충전된 경우 사용되는 컴파일 필터입니다. 프로필 기반 컴파일을 활용하고 저장용량을 절약하려면 speed-profile 컴파일러 필터를 사용해 봅니다.

  • pm.dexopt.boot=verify
  • 무선 업데이트 후 사용되는 컴파일 필터입니다. 부팅 시간이 아주 길어지는 것을 방지하기 위해 이 옵션에는 verify 컴파일러 필터를 사용하는 것을 적극 권장합니다.

  • pm.dexopt.first-boot=quicken
  • 처음 기기를 부팅할 때 사용되는 컴파일 필터입니다. 여기에 사용되는 필터는 공장 출고 이후의 부팅 시간에만 영향을 줍니다. 사용자가 스마트폰을 처음 사용하게 되기 전에, 부팅 시간이 아주 길어지는 것을 방지하기 위해 이 옵션에는 quicken 필터를 사용하는 것이 권장됩니다. /system의 모든 애플리케이션이 이미 quicken 컴파일러 필터로 컴파일되었거나 speed 또는 speed-profile 컴파일러 필터로 컴파일된 경우 pm.dexopt.first-boot는 아무런 영향을 주지 않습니다.

Dex2oat 옵션

Dex2oat 옵션은 사전 최적화뿐 아니라 기기상의 컴파일 중에도 dex2oat에 영향을 미치지만 위에 설명된 대부분의 옵션은 사전 최적화에만 영향을 미칩니다.

부팅 이미지를 컴파일하는 동안 dex2oat를 제어하는 방법

  • dalvik.vm.image-dex2oat-Xms: 초기 힙 크기
  • dalvik.vm.image-dex2oat-Xmx: 최대 힙 크기
  • dalvik.vm.image-dex2oat-filter: 컴파일러 필터 옵션
  • dalvik.vm.image-dex2oat-threads: 사용할 스레드 수

부팅 이미지 외의 모든 항목을 컴파일하는 동안 dex2oat를 제어하는 방법

  • dalvik.vm.dex2oat-Xms: 초기 힙 크기
  • dalvik.vm.dex2oat-Xmx: 최대 힙 크기
  • dalvik.vm.dex2oat-filter: 컴파일러 필터 옵션

Android 6.0까지 출시에서는 부팅 이미지 외의 모든 항목을 컴파일할 수 있는 추가 옵션이 한 가지 제공됩니다.

  • dalvik.vm.dex2oat-threads: 사용할 스레드 수

Android 6.1부터는 부팅 이미지 외에 모든 항목을 컴파일할 수 있는 추가 옵션이 두 개로 늘었습니다.

  • dalvik.vm.boot-dex2oat-threads: 부팅 시 사용할 스레드 수
  • dalvik.vm.dex2oat-threads: 부팅 후 사용할 스레드 수

Android 7.1부터는 부팅 이미지 외의 모든 항목을 컴파일할 때 메모리를 어떻게 사용할지 제어할 수 있는 두 가지 옵션이 제공됩니다.

  • dalvik.vm.dex2oat-very-large: AOT 컴파일을 사용 중지하기 위한 총 dex 파일 크기 최솟값(단위: 바이트)
  • dalvik.vm.dex2oat-swap: dex2oat 스왑 파일 사용(메모리 용량이 낮은 기기용)

dex2oat의 초기 힙 크기와 최대 힙 크기를 제어하는 옵션은 컴파일 가능한 애플리케이션을 제한할 수 있으므로 줄여서는 안 됩니다.

Android 11부터 컴파일러 스레드를 특정 CPU 그룹으로 제한할 수 있도록 다음의 세 가지 CPU 선호도 옵션이 제공됩니다.

  • dalvik.vm.boot-dex2oat-cpu-set: 부팅 시 dex2oat 스레드를 실행하는 CPU
  • dalvik.vm.image-dex2oat-cpu-set: 부팅 이미지를 컴파일하는 동안 dex2oat를 실행하는 CPU
  • dalvik.vm.dex2oat-cpu-set: 부팅 후 dex2oat 스레드를 실행하는 CPU

CPU는 쉼표로 구분된 CPU ID 목록으로 지정해야 합니다. 예를 들어 CPU 0-3의 dex2oat에서 실행하려면 다음과 같이 설정해야 합니다.

dalvik.vm.dex2oat-cpu-set=0,1,2,3

CPU 선호도 속성을 설정할 때에는 불필요한 메모리 및 I/O 경합을 방지하기 위해 선택된 CPU 수와 같도록 dex2oat 스레드 수에 상응하는 속성을 일치시키는 것이 좋습니다.

dalvik.vm.dex2oat-cpu-set=0,1,2,3
dalvik.vm.dex2oat-threads=4

Android 12부터 다음 옵션이 추가되었습니다.

  • dalvik.vm.ps-min-first-save-ms: 애플리케이션이 처음 실행될 때 런타임이 애플리케이션 프로필을 생성할 때까지 기다리는 시간입니다.
  • dalvik.vm.ps-min-save-period-ms: 앱 프로필을 업데이트하기 전에 기다려야 하는 최소 시간입니다.
  • dalvik.vm.systemservercompilerfilter: 시스템 서버를 다시 컴파일할 때 기기에서 사용하는 컴파일러 필터입니다.

A/B별 구성

ROM 구성

Android 7.0부터 기기는 두 개의 시스템 파티션을 사용하여 A/B 시스템 업데이트를 사용 설정할 수 있습니다. 시스템 파티션 크기를 절약하려면 사전 선택된 파일을 사용되지 않은 두 번째 시스템 파티션에 설치하면 됩니다. 그러면 그러한 파일은 처음 부팅 시 데이터 파티션에 복사됩니다.

사용 예(device-common.mk에서):

PRODUCT_PACKAGES += \
     cppreopts.sh
PRODUCT_PROPERTY_OVERRIDES += \
     ro.cp_system_other_odex=1

기기의 BoardConfig.mk에서:

BOARD_USES_SYSTEM_OTHER_ODEX := true

부트 클래스 경로 코드, 시스템 서버 코드, 제품별 핵심 애플리케이션은 항상 시스템 파티션에 컴파일됩니다. 기본적으로 다른 모든 애플리케이션은 사용되지 않은 두 번째 시스템 파티션에 컴파일됩니다. 이는 SYSTEM_OTHER_ODEX_FILTER를 사용하여 제어할 수 있으며 이 필터의 기본값은 다음과 같습니다.

SYSTEM_OTHER_ODEX_FILTER ?= app/% priv-app/%

백그라운드 dexopt OTA

A/B 지원 기기를 사용할 경우 애플리케이션은 새 시스템 이미지로 업데이트되도록 백그라운드에서 컴파일할 수 있습니다. 시스템 이미지에 컴파일 스크립트와 바이너리를 선택적으로 포함하려면 백그라운드에서 앱 컴파일을 참조하세요. 이 컴파일에 사용되는 컴파일 필터는 다음을 통해 제어됩니다.

pm.dexopt.ab-ota=speed-profile

프로필 기반 컴파일을 활용하고 저장용량을 절약하기 위해 speed-profile을 사용하는 것이 권장됩니다.