Konfigurasi Layanan ART

Sebelum memulai, lihat ringkasan umum Layanan ART.

Mulai Android 14, kompilasi AOT di perangkat untuk aplikasi (alias dexopt) ditangani oleh Layanan ART. Layanan ART adalah bagian dari modul ART, dan Anda dapat menyesuaikannya melalui properti dan API sistem.

Properti sistem

Layanan ART mendukung semua opsi dex2oat yang relevan.

Selain itu, Layanan ART mendukung properti sistem berikut:

pm.dexopt.<reason>

Ini adalah serangkaian properti sistem yang menentukan filter compiler default untuk semua alasan kompilasi yang telah ditentukan sebelumnya yang dijelaskan dalam Skenario dexopt.

Untuk mengetahui informasi selengkapnya, lihat Filter compiler.

Nilai default standar adalah:

pm.dexopt.first-boot=verify
pm.dexopt.boot-after-ota=verify
pm.dexopt.boot-after-mainline-update=verify
pm.dexopt.bg-dexopt=speed-profile
pm.dexopt.inactive=verify
pm.dexopt.cmdline=verify

pm.dexopt.shared (default: speed)

Ini adalah filter compiler penggantian untuk aplikasi yang digunakan oleh aplikasi lain.

Pada prinsipnya, Layanan ART melakukan kompilasi yang dipandu profil (speed-profile) untuk semua aplikasi jika memungkinkan, biasanya selama dexopt latar belakang. Namun, ada beberapa aplikasi yang digunakan oleh aplikasi lain (baik melalui <uses-library> atau dimuat secara dinamis menggunakan Context#createPackageContext dengan CONTEXT_INCLUDE_CODE). Aplikasi tersebut tidak dapat menggunakan profil lokal karena alasan privasi.

Untuk aplikasi semacam itu, jika kompilasi yang dipandu profil diminta, Layanan ART akan mencoba menggunakan profil cloud terlebih dahulu. Jika profil cloud tidak ada, Layanan ART akan menggunakan filter compiler yang ditentukan oleh pm.dexopt.shared.

Jika kompilasi yang diminta tidak dipandu profil, properti ini tidak akan berpengaruh.

pm.dexopt.<reason>.concurrency (default: 1)

Ini adalah jumlah pemanggilan dex2oat untuk alasan kompilasi yang telah ditentukan sebelumnya (first-boot, boot-after-ota, boot-after-mainline-update, dan bg-dexopt).

Perhatikan bahwa efek opsi ini digabungkan dengan opsi penggunaan resource dex2oat (dalvik.vm.*dex2oat-threads, dalvik.vm.*dex2oat-cpu-set, dan profil tugas):

  • dalvik.vm.*dex2oat-threads mengontrol jumlah thread untuk setiap pemanggilan dex2oat, sedangkan pm.dexopt.<reason>.concurrency mengontrol jumlah pemanggilan dex2oat. Artinya, jumlah maksimum thread serentak adalah hasil perkalian dari dua properti sistem.
  • dalvik.vm.*dex2oat-cpu-set dan profil tugas selalu membatasi penggunaan core CPU, terlepas dari jumlah maksimum thread serentak (yang dibahas di atas).

Satu pemanggilan dex2oat mungkin tidak sepenuhnya menggunakan semua core CPU, terlepas dari dalvik.vm.*dex2oat-threads. Oleh karena itu, meningkatkan jumlah pemanggilan dex2oat (pm.dexopt.<reason>.concurrency) dapat memanfaatkan core CPU dengan lebih baik, untuk mempercepat progres dexopt secara keseluruhan. Hal ini sangat berguna selama proses booting.

Namun, terlalu banyak pemanggilan dex2oat dapat menyebabkan perangkat kehabisan memori, meskipun hal ini dapat diatasi dengan menyetel dalvik.vm.dex2oat-swap ke true untuk mengizinkan penggunaan file swap. Terlalu banyak pemanggilan juga dapat menyebabkan pengalihan konteks yang tidak perlu. Oleh karena itu, angka ini harus disesuaikan dengan cermat berdasarkan produk.

pm.dexopt.downgrade_after_inactive_days (default: tidak disetel)

Jika opsi ini disetel, Layanan ART hanya mengoptimalkan aplikasi yang digunakan dalam jumlah hari terakhir yang ditentukan.

Selain itu, jika penyimpanan hampir penuh, selama dexopt latar belakang, Layanan ART akan menurunkan versi filter compiler aplikasi yang tidak digunakan dalam jumlah hari terakhir yang ditentukan, untuk mengosongkan ruang. Alasan compiler untuk hal ini adalah inactive, dan filter compiler ditentukan oleh pm.dexopt.inactive. Ambang batas ruang untuk memicu fitur ini adalah ambang batas ruang rendah Pengelola Penyimpanan (dapat dikonfigurasi melalui setelan global sys_storage_threshold_percentage dan sys_storage_threshold_max_bytes, default: 500 MB) ditambah 500 MB.

Jika Anda menyesuaikan daftar paket melalui ArtManagerLocal#setBatchDexoptStartCallback, paket dalam daftar yang disediakan oleh BatchDexoptStartCallback untuk bg-dexopt tidak akan pernah di-downgrade.

pm.dexopt.disable_bg_dexopt (default: false)

Ini hanya untuk pengujian. Tindakan ini mencegah Layanan ART menjadwalkan tugas dexopt latar belakang.

Jika tugas dexopt latar belakang sudah dijadwalkan tetapi belum dijalankan, opsi ini tidak akan berpengaruh. Artinya, tugas akan tetap berjalan.

Urutan perintah yang direkomendasikan untuk mencegah tugas dexopt latar belakang berjalan adalah:

setprop pm.dexopt.disable_bg_dexopt true
pm bg-dexopt-job --disable

Baris pertama mencegah tugas pengoptimalan dex di latar belakang dijadwalkan, jika belum dijadwalkan. Baris kedua membatalkan penjadwalan tugas dexopt latar belakang, jika tugas tersebut sudah dijadwalkan, dan membatalkan tugas dexopt latar belakang dengan segera, jika tugas tersebut sedang berjalan.

ART Service API

Layanan ART mengekspos Java API untuk penyesuaian. API ditentukan dalam ArtManagerLocal. Lihat Javadoc di art/libartservice/service/java/com/android/server/art/ArtManagerLocal.java untuk penggunaan (sumber Android 14, sumber pengembangan yang belum dirilis).

ArtManagerLocal adalah singleton yang dipegang oleh LocalManagerRegistry. Fungsi pembantu com.android.server.pm.DexOptHelper#getArtManagerLocal membantu Anda mendapatkannya.

import static com.android.server.pm.DexOptHelper.getArtManagerLocal;

Sebagian besar API memerlukan instance PackageManagerLocal.FilteredSnapshot, yang menyimpan informasi semua aplikasi. Anda bisa mendapatkannya dengan memanggil PackageManagerLocal#withFilteredSnapshot, dengan PackageManagerLocal juga merupakan singleton yang dipegang oleh LocalManagerRegistry dan dapat diperoleh dari com.android.server.pm.PackageManagerServiceUtils#getPackageManagerLocal.

import static com.android.server.pm.PackageManagerServiceUtils.getPackageManagerLocal;

Berikut adalah beberapa kasus penggunaan umum API.

Memicu dexopt untuk aplikasi

Anda dapat memicu dexopt untuk aplikasi apa pun kapan saja dengan memanggil ArtManagerLocal#dexoptPackage.

try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  getArtManagerLocal().dexoptPackage(
      snapshot,
      "com.google.android.calculator",
      new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build());
}

Anda juga dapat meneruskan alasan dexopt Anda sendiri. Jika Anda melakukannya, class prioritas dan filter compiler harus ditetapkan secara eksplisit.

try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  getArtManagerLocal().dexoptPackage(
      snapshot,
      "com.google.android.calculator",
      new DexoptParams.Builder("my-reason")
          .setCompilerFilter("speed-profile")
          .setPriorityClass(ArtFlags.PRIORITY_BACKGROUND)
          .build());
}

Membatalkan dexopt

Jika operasi dimulai dengan panggilan dexoptPackage, Anda dapat meneruskan sinyal pembatalan, yang memungkinkan Anda membatalkan operasi pada suatu saat. Hal ini dapat berguna saat Anda menjalankan dexopt secara asinkron.

Executor executor = ...;  // Your asynchronous executor here.
var cancellationSignal = new CancellationSignal();
executor.execute(() -> {
  try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
    getArtManagerLocal().dexoptPackage(
        snapshot,
        "com.google.android.calculator",
        new DexoptParams.Builder(ReasonMapping.REASON_INSTALL).build(),
        cancellationSignal);
  }
});

// When you want to cancel the operation.
cancellationSignal.cancel();

Anda juga dapat membatalkan dexopt di latar belakang, yang dimulai oleh Layanan ART.

getArtManagerLocal().cancelBackgroundDexoptJob();

Mendapatkan hasil dexopt

Jika operasi dimulai dengan panggilan dexoptPackage, Anda bisa mendapatkan hasilnya dari nilai yang ditampilkan.

DexoptResult result;
try (var snapshot = getPackageManagerLocal().withFilteredSnapshot()) {
  result = getArtManagerLocal().dexoptPackage(...);
}

// Process the result here.
...

Layanan ART juga memulai operasi dexopt itu sendiri dalam banyak skenario, seperti dexopt latar belakang. Untuk memproses semua hasil dexopt, baik operasi dimulai dengan panggilan dexoptPackage atau oleh Layanan ART, gunakan ArtManagerLocal#addDexoptDoneCallback.

getArtManagerLocal().addDexoptDoneCallback(
    false /* onlyIncludeUpdates */,
    Runnable::run,
    (result) -> {
      // Process the result here.
      ...
    });

Argumen pertama menentukan apakah hanya menyertakan pembaruan dalam hasil. Jika Anda hanya ingin memantau paket yang diupdate oleh dexopt, tetapkan ke benar.

Argumen kedua adalah eksekutor callback. Untuk menjalankan callback di thread yang sama yang menjalankan dexopt, gunakan Runnable::run. Jika Anda tidak ingin callback memblokir dexopt, gunakan eksekutor asinkron.

Anda dapat menambahkan beberapa callback, dan Layanan ART akan mengeksekusi semuanya secara berurutan. Semua callback akan tetap aktif untuk semua panggilan mendatang kecuali Anda menghapusnya.

Jika Anda ingin menghapus callback, simpan referensi callback saat Anda menambahkannya, dan gunakan ArtManagerLocal#removeDexoptDoneCallback.

DexoptDoneCallback callback = (result) -> {
  // Process the result here.
  ...
};

getArtManagerLocal().addDexoptDoneCallback(
    false /* onlyIncludeUpdates */, Runnable::run, callback);

// When you want to remove it.
getArtManagerLocal().removeDexoptDoneCallback(callback);

Menyesuaikan daftar paket dan parameter dexopt

Layanan ART memulai operasi dexopt sendiri selama booting dan dexopt latar belakang. Untuk menyesuaikan daftar paket atau parameter dexopt untuk operasi tersebut, gunakan ArtManagerLocal#setBatchDexoptStartCallback.

getArtManagerLocal().setBatchDexoptStartCallback(
    Runnable::run,
    (snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
      switch (reason) {
        case ReasonMapping.REASON_BG_DEXOPT:
          var myPackages = new ArrayList<String>(defaultPackages);
          myPackages.add(...);
          myPackages.remove(...);
          myPackages.sort(...);
          builder.setPackages(myPackages);
          break;
        default:
          // Ignore unknown reasons.
      }
    });

Anda dapat menambahkan item ke daftar paket, menghapus item dari daftar, mengurutkannya, atau bahkan menggunakan daftar yang sama sekali berbeda.

Panggilan balik Anda harus mengabaikan alasan yang tidak diketahui karena lebih banyak alasan dapat ditambahkan pada masa mendatang.

Anda dapat menetapkan paling banyak satu BatchDexoptStartCallback. Callback akan tetap aktif untuk semua panggilan mendatang kecuali jika Anda menghapusnya.

Jika Anda ingin menghapus callback, gunakan ArtManagerLocal#clearBatchDexoptStartCallback.

getArtManagerLocal().clearBatchDexoptStartCallback();

Menyesuaikan parameter tugas dexopt latar belakang

Secara default, tugas dexopt latar belakang berjalan sekali sehari saat perangkat dalam kondisi tidak digunakan dan sedang diisi dayanya. Setelan ini dapat diubah menggunakan ArtManagerLocal#setScheduleBackgroundDexoptJobCallback.

getArtManagerLocal().setScheduleBackgroundDexoptJobCallback(
    Runnable::run,
    builder -> {
      builder.setPeriodic(TimeUnit.DAYS.toMillis(2));
    });

Anda dapat menetapkan paling banyak satu ScheduleBackgroundDexoptJobCallback. Callback akan tetap aktif untuk semua panggilan mendatang kecuali jika Anda menghapusnya.

Jika Anda ingin menghapus callback, gunakan ArtManagerLocal#clearScheduleBackgroundDexoptJobCallback.

getArtManagerLocal().clearScheduleBackgroundDexoptJobCallback();

Menonaktifkan dexopt untuk sementara

Setiap operasi dexopt yang dimulai oleh Layanan ART akan memicu BatchDexoptStartCallback. Anda dapat terus membatalkan operasi untuk menonaktifkan dexopt secara efektif.

Jika operasi yang Anda batalkan adalah dexopt latar belakang, operasi tersebut akan mengikuti kebijakan percobaan ulang default (30 detik, eksponensial, dibatasi hingga 5 jam).

// Good example.

var shouldDisableDexopt = new AtomicBoolean(false);

getArtManagerLocal().setBatchDexoptStartCallback(
    Runnable::run,
    (snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
      if (shouldDisableDexopt.get()) {
        cancellationSignal.cancel();
      }
    });

// Disable dexopt.
shouldDisableDexopt.set(true);
getArtManagerLocal().cancelBackgroundDexoptJob();

// Re-enable dexopt.
shouldDisableDexopt.set(false);

Anda dapat memiliki maksimal satu BatchDexoptStartCallback. Jika Anda juga ingin menggunakan BatchDexoptStartCallback untuk menyesuaikan daftar paket atau parameter dexopt, Anda harus menggabungkan kode ke dalam satu callback.

// Bad example.

// Disable dexopt.
getArtManagerLocal().unscheduleBackgroundDexoptJob();

// Re-enable dexopt.
getArtManagerLocal().scheduleBackgroundDexoptJob();

Operasi dexopt yang dilakukan saat penginstalan aplikasi tidak dimulai oleh Layanan ART. Sebagai gantinya, proses ini dimulai oleh pengelola paket melalui panggilan dexoptPackage. Oleh karena itu, tidak memicu BatchDexoptStartCallback. Untuk menonaktifkan dexopt saat penginstalan aplikasi, cegah pengelola paket memanggil dexoptPackage.

Mengganti filter compiler untuk paket tertentu (Android 15+)

Anda dapat mengganti filter compiler untuk paket tertentu dengan mendaftarkan callback melalui setAdjustCompilerFilterCallback. Callback dipanggil setiap kali paket akan di-dexopt, terlepas dari apakah dexopt dimulai oleh Layanan ART selama booting dan dexopt latar belakang atau oleh panggilan API dexoptPackage.

Jika paket tidak perlu disesuaikan, callback harus menampilkan originalCompilerFilter.

getArtManagerLocal().setAdjustCompilerFilterCallback(
    Runnable::run,
    (packageName, originalCompilerFilter, reason) -> {
      if (isVeryImportantPackage(packageName)) {
        return "speed-profile";
      }
      return originalCompilerFilter;
    });

Anda hanya dapat menyetel satu AdjustCompilerFilterCallback. Jika Anda ingin menggunakan AdjustCompilerFilterCallback untuk mengganti filter compiler untuk beberapa paket, Anda harus menggabungkan kode ke dalam satu callback. Callback tetap aktif untuk semua panggilan mendatang, kecuali jika Anda menghapusnya.

Jika Anda ingin menghapus callback, gunakan ArtManagerLocal#clearAdjustCompilerFilterCallback.

getArtManagerLocal().clearAdjustCompilerFilterCallback();

Penyesuaian lainnya

Layanan ART juga mendukung beberapa penyesuaian lainnya.

Menetapkan batas termal untuk dexopt latar belakang

Kontrol termal tugas dexopt latar belakang dilakukan oleh Job Scheduler. Tugas akan segera dibatalkan saat suhu mencapai THERMAL_STATUS_MODERATE. Ambang batas THERMAL_STATUS_MODERATE dapat disesuaikan.

Menentukan apakah dexopt latar belakang sedang berjalan

Tugas dexopt latar belakang dikelola oleh Job Scheduler, dan ID tugasnya adalah 27873780. Untuk menentukan apakah tugas sedang berjalan, gunakan API Job Scheduler.

// Good example.

var jobScheduler =
    Objects.requireNonNull(mContext.getSystemService(JobScheduler.class));
int reason = jobScheduler.getPendingJobReason(27873780);

if (reason == PENDING_JOB_REASON_EXECUTING) {
  // Do something when the job is running.
  ...
}
// Bad example.

var backgroundDexoptRunning = new AtomicBoolean(false);

getArtManagerLocal().setBatchDexoptStartCallback(
    Runnable::run,
    (snapshot, reason, defaultPackages, builder, cancellationSignal) -> {
      if (reason.equals(ReasonMapping.REASON_BG_DEXOPT)) {
        backgroundDexoptRunning.set(true);
      }
    });

getArtManagerLocal().addDexoptDoneCallback(
    false /* onlyIncludeUpdates */,
    Runnable::run,
    (result) -> {
      if (result.getReason().equals(ReasonMapping.REASON_BG_DEXOPT)) {
        backgroundDexoptRunning.set(false);
      }
    });

if (backgroundDexoptRunning.get()) {
  // Do something when the job is running.
  ...
}

Menyediakan profil untuk dexopt

Untuk menggunakan profil guna memandu dexopt, letakkan file .prof atau file .dm di samping APK.

File .prof harus berupa file profil format biner, dan nama file harus berupa nama file APK + .prof. Misalnya,

base.apk.prof

Nama file .dm harus berupa nama file APK dengan ekstensi yang diganti dengan .dm. Misalnya,

base.dm

Untuk memverifikasi bahwa profil sedang digunakan untuk dexopt, jalankan dexopt dengan speed-profile dan periksa hasilnya.

pm art clear-app-profiles <package-name>
pm compile -m speed-profile -f -v <package-name>

Baris pertama menghapus semua profil yang dihasilkan oleh runtime (yaitu, yang ada di /data/misc/profiles), jika ada, untuk memastikan bahwa profil di samping APK adalah satu-satunya profil yang dapat digunakan oleh Layanan ART. Baris kedua menjalankan dexopt dengan speed-profile, dan meneruskan -v untuk mencetak hasil verbose.

Jika profil sedang digunakan, Anda akan melihat actualCompilerFilter=speed-profile di hasilnya. Jika tidak, Anda akan melihat actualCompilerFilter=verify. Misalnya,

DexContainerFileDexoptResult{dexContainerFile=/data/app/~~QR0fTV0UbDbIP1Su7XzyPg==/com.google.android.gms-LvusF2uARKOtBbcaPHdUtQ==/base.apk, primaryAbi=true, abi=x86_64, actualCompilerFilter=speed-profile, status=PERFORMED, dex2oatWallTimeMillis=4549, dex2oatCpuTimeMillis=14550, sizeBytes=3715344, sizeBeforeBytes=3715344}

Alasan umum mengapa Layanan ART tidak menggunakan profil meliputi:

  • Profil memiliki nama file yang salah atau tidak berada di samping APK.
  • Profil menggunakan format yang salah.
  • Profil tidak cocok dengan APK. (Checksum dalam profil tidak cocok dengan checksum file .dex dalam APK.)