Menjaga pekerjaan latar belakang tetap aktif dengan proses yang efisien

Banyak aplikasi melakukan pekerjaan yang sah saat UI-nya tidak lagi terlihat, seperti mendownload paket aset besar, menyinkronkan database lokal, melakukan streaming audio, merekam latihan olahraga, atau mempertahankan sesi perangkat yang terhubung. Saat pekerjaan latar belakang ini berjalan di dalam proses OS yang sama dengan UI latar depan dan mesin grafis, seluruh proses tetap berada pada tingkat kepentingan yang lebih tinggi (oom_score_adj antara 0 dan 250) bersama dengan semua memori yang dialokasikan UI.

Halaman ini menjelaskan alasan hal tersebut merugikan aplikasi Anda sendiri serta perangkat lainnya, cara memindahkan pekerjaan latar belakang ke proses ringan khusus menggunakan android:process, dan cara memverifikasi penghematan memori menggunakan adb shell dan Perfetto.

Alasan pekerjaan satu proses mempertahankan memori UI

Android mengelola memori dan penghentian proses pada tingkat perincian proses OS (pid), bukan komponen individual (Activity, Service, atau BroadcastReceiver). Activity Manager (OomAdjuster) menetapkan satu nilai oom_score_adj untuk setiap proses yang mencerminkan komponen aktif atau binding masuk yang paling penting dalam proses tersebut:

  • Saat pengguna berinteraksi dengan Activity Anda, proses akan berjalan dalam status TOP (oom_score_adj = 0).
  • Saat pengguna keluar dan aplikasi Anda tidak memiliki komponen latar belakang yang aktif, proses akan berpindah ke bucket PREV (oom_score_adj = 700) atau CACHED (oom_score_adj >= 900). Dalam bucket yang di-cache, platform membekukan proses dengan freezer cgroup v2, memadatkan memorinya dengan CachedAppOptimizer, dan memungkinkan lmkd menghentikan proses terlebih dahulu di bawah tekanan memori.
  • Namun, jika proses yang sama juga menjalankan layanan latar depan (seperti jenis dataSync, mediaPlayback, connectedDevice, atau location, di oom_score_adj = 200) atau menghosting layanan yang terikat dengan sistem atau klien penting lainnya, seluruh proses akan tetap berada pada prioritas yang ditingkatkan tersebut bahkan setelah Activity dihentikan.
Perbandingan penempatan bersama satu proses yang mempertahankan memori UI tetap disematkan
       pada oom_score_adj 200 versus isolasi proses terpisah yang hanya mempertahankan
       worker pada oom_score_adj 200 sementara proses UI turun ke cache.
Gambar 1. Penempatan bersama satu proses dibandingkan dengan isolasi proses terpisah selama pekerjaan latar belakang.

Saat proses UI yang berat tetap berada di oom_score_adj = 200, beberapa kategori memori yang besar tetap berada di memori:

  • Activity dihentikan dan hierarki tampilan: Kecuali jika Activity diselesaikan secara eksplisit, Window, hierarki tampilan, struktur tata letak, dan status tampilannya tetap dapat dijangkau di heap Java saat pengguna berada di aplikasi lain.
  • Konteks grafis native dan alokasi GPU: HardwareRenderer standar mempertahankan konteks dan cache GPU-nya, sedangkan Activity yang dihentikan berada dalam proses yang tidak di-cache. Aplikasi dan game engine yang membuat konteks Vulkan (VkDevice, VkSwapchainKHR, kumpulan deskriptor, cache pipeline) atau OpenGL ES (EGLContext, tekstur, buffer vertex) sendiri juga mempertahankan struktur driver, program shader, dan buffer grafis, kecuali jika engine secara eksplisit menghapus perangkat grafis saat dihentikan.
  • Fragmentasi heap dan overhead runtime: Meskipun aplikasi merilis cache di onTrimMemory, halaman pengalokasi native yang terisi sebagian, halaman copy-on-write Zygote yang kotor, library bersama yang dimuat (.so), dan metadata runtime tetap berada di memori pribadi proses. Karena prosesnya berada di atas batas yang di-cache (oom_score_adj < 900), platform tidak akan membekukannya atau menjalankan pemadatan aplikasi yang di-cache. Untuk melihat lebih dekat efek ini dengan layanan terikat, lihat Menempatkan layanan terikat bersama UI yang menggunakan banyak memori.

Cara proses latar belakang yang efisien melindungi aplikasi Anda

Menyimpan memori berukuran latar depan dalam status latar belakang akan membahayakan pekerjaan latar belakang Anda sendiri:

  • Batas memori latar belakang lebih ketat daripada batas latar depan. Di Android 17 dan yang lebih baru, Pembatas Memori menerapkan batas memori tidak terlihat yang lebih kecil ke aplikasi Anda saat tidak ada prosesnya yang menampilkan UI yang terlihat, termasuk saat layanan latar depan berjalan. Batas ini mencakup semua proses aplikasi Anda secara bersamaan. Jika proses yang menjalankan layanan latar depan Anda masih menyimpan UI dan memori grafisnya, memori tersebut akan dihitung terhadap batas yang lebih kecil. Saat aplikasi Anda melebihi batas, kernel akan membatasinya saat mengklaim kembali halamannya, yang akan memperlambat download, sinkronisasi, atau pemutaran yang sedang berjalan. Jika aplikasi Anda terus mengalokasikan setelah swap habis, sistem akan menghentikannya. Untuk memeriksa apakah aplikasi Anda terpengaruh, lihat Batas memori aplikasi.
  • Proses yang lebih besar akan dihentikan terlebih dahulu saat terjadi tekanan memori. Saat lmkd harus mengosongkan memori dan dikonfigurasi untuk memilih proses terberat dalam bucket kepentingan (ro.lmk.kill_heaviest_task), proses layanan latar depan yang juga menyimpan ratusan MB memori UI akan dihentikan sebelum proses yang lebih kecil dengan kepentingan yang sama, dan pekerjaan latar belakang Anda akan berhenti bersamanya. Lihat interaksi kswapd dan lmkd.
  • Status UI yang di-cache dapat dibekukan, dipadatkan, dan ditulis kembali ke flash. Dengan mengizinkan status UI latar depan Anda berpindah ke proses yang di-cache, OS dapat membekukan proses, memadatkan memorinya ke dalam zRAM, dan menulis kembali halaman dinginnya ke penyimpanan flash. Jejak memori residennya menyusut sementara status UI Anda tetap utuh, sehingga memberikan peluang terbaik bagi pekerjaan latar belakang dan UI yang di-cache untuk bertahan hingga pengguna kembali.
  • Anggaran memori per proses menjadi praktis. Proses yang hanya menjalankan pekerjaan latar belakang memiliki set kerja yang kecil dan dapat diprediksi, sehingga Anda dapat memberinya anggaran memori yang ketat tanpa membatasi UI Anda.

Menjalankan proses kedua mungkin terlihat seperti meningkatkan total jejak memori aplikasi Anda, karena runtime kedua memiliki biaya dasar pengukuran yang kecil. Perbandingan tersebut hanya berlaku jika satu proses dapat sepenuhnya mengosongkan semua status UI dan grafisnya setiap kali beralih ke pekerjaan latar belakang, yang tidak dapat dilakukan oleh aplikasi dan toolkit UI dengan andal dalam praktiknya. Membiarkan OS membekukan, memadatkan, dan menulis kembali proses UI yang di-cache secara terpisah akan menggunakan lebih sedikit memori fisik dalam kasus umum, menjaga aplikasi Anda tetap dalam batas tidak terlihat, dan mengurangi penghentian karena memori rendah untuk aplikasi Anda dan di seluruh perangkat.

Mengisolasi pekerjaan latar belakang dalam proses khusus

Untuk menghilangkan residensi UI dan grafis selama pekerjaan latar belakang, jalankan komponen latar belakang yang berjalan lama dalam proses ringan khusus sambil mempertahankan UI dan mesin grafis Anda dalam proses utama:

Proses Deklarasi manifes Komponen yang dihosting Status saat pengguna keluar dari UI selama pekerjaan latar belakang
Proses UI utama Default (com.example.app) Activity, tampilan UI, cache gambar, perender Vulkan atau OpenGL ES, mesin audio dan scene Turun ke PREV, lalu CACHED (oom_score_adj >= 900). Dibekukan, dipadatkan, dan ditulis kembali ke flash.
Proses worker latar belakang android:process=":bg" Service latar depan, downloader aset latar belakang, worker sinkronisasi, atau pengelola perangkat terhubung Berjalan di FGS (oom_score_adj = 200) hanya dengan heap dan kode worker itu sendiri, serta tanpa tampilan UI atau konteks GPU.

Mendeklarasikan anggaran proses dan memori

Tetapkan Service latar belakang Anda ke subproses pribadi dengan menetapkan android:process=":bg" di AndroidManifest.xml. Titik dua di awal (:) membuat proses pribadi yang dicakup dalam UID aplikasi Anda.

Di Android 17 QPR2 (API level 37.2) dan yang lebih tinggi, gabungkan pemisahan proses dengan anggaran memori aplikasi per proses di dalam blok <processes> sehingga sistem operasi menerapkan batas set kerja yang ketat pada worker latar belakang tanpa membatasi proses UI latar depan Anda:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        android:name=".HeavyApplication"
        android:label="@string/app_name">

        <!-- App-wide budgets that cover every process, including :bg -->
        <memory-budget android:maxMb="512" />
        <memory-budget
            android:maxMb="128"
            android:state="background" />

        <processes>
            <!-- Tight budget for the headless background worker process -->
            <process android:process=":bg">
                <memory-budget android:maxMb="32" />
            </process>
        </processes>

        <activity
            android:name=".MainActivity"
            android:exported="true" />

        <service
            android:name=".AssetDownloadService"
            android:process=":bg"
            android:foregroundServiceType="dataSync"
            android:exported="false" />

    </application>
</manifest>

Penggunaan memori di :bg dihitung terhadap anggarannya sendiri dan anggaran seluruh aplikasi. Pada perangkat yang menjalankan versi sebelum Android 17 QPR2, sistem akan mengabaikan <memory-budget>, sedangkan android:process=":bg" berfungsi di semua level API.

Inisialisasi gate di Application.onCreate

Android membuat instance class Application Anda dan memanggil Application.onCreate di setiap proses yang dihasilkan untuk paket Anda. Kesalahan umum saat mengadopsi android:process=":bg" adalah membiarkan toolkit UI yang berat, kumpulan pemuatan gambar, Analytics SDK, atau inisialisasi mesin native (System.loadLibrary) di dalam metode Application.onCreate yang tidak terlindungi. Tindakan ini akan meningkatkan proses :bg dengan jejak dasar pengukuran yang sama dengan yang ingin Anda hindari.

Periksa Application.getProcessName, yang tersedia di Android 9 (level API 28) dan yang lebih tinggi, di dalam onCreate dan hanya lakukan inisialisasi dependensi yang diperlukan oleh proses saat ini:

Kotlin

class HeavyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        val processName = Application.getProcessName()
        if (processName != null && processName.endsWith(BG_PROCESS_SUFFIX)) {
            initBackgroundWorkerProcess()
            return
        }
        initMainUiProcess()
    }

    private fun initBackgroundWorkerProcess() {
        // Initialize only lightweight HTTP, storage, and sync primitives.
        // Avoid loading native rendering libraries or allocating UI caches.
    }

    private fun initMainUiProcess() {
        // Initialize UI frameworks, image caches, and native engines.
        System.loadLibrary("heavy_engine_jni")
    }

    companion object {
        private const val BG_PROCESS_SUFFIX = ":bg"
    }
}

Java

public final class HeavyApplication extends Application {
    private static final String BG_PROCESS_SUFFIX = ":bg";

    @Override
    public void onCreate() {
        super.onCreate();
        String processName = Application.getProcessName();
        if (processName != null && processName.endsWith(BG_PROCESS_SUFFIX)) {
            initBackgroundWorkerProcess();
            return;
        }
        initMainUiProcess();
    }

    private void initBackgroundWorkerProcess() {
        // Initialize only lightweight HTTP, storage, and sync primitives.
        // Avoid loading native rendering libraries or allocating UI caches.
    }

    private void initMainUiProcess() {
        // Initialize UI frameworks, image caches, and native engines.
        System.loadLibrary("heavy_engine_jni");
    }
}

Library yang melakukan inisialisasi melalui penyedia konten, seperti Jetpack App Startup, hanya melakukan inisialisasi dalam proses utama, karena penyedia konten yang dideklarasikan tanpa android:process berjalan dalam proses utama. Jika kode di :bg bergantung pada library tersebut, lakukan inisialisasi secara eksplisit di initBackgroundWorkerProcess.

Jika Anda membangun dengan game engine seperti Unity, terapkan layanan latar belakang dalam library atau plugin Android biasa yang ditulis dalam Java atau Kotlin yang tidak mereferensikan class engine seperti UnityPlayer. Proses :bg kemudian tidak pernah memuat library native mesin. Untuk mengonfirmasi, pastikan adb shell cat /proc/<pid>/maps untuk proses :bg tidak mencantumkan libunity.so atau libil2cpp.so.

Berkomunikasi tanpa menyematkan proses UI

Saat pengguna membuka MainActivity saat AssetDownloadService berjalan di :bg, UI sering kali perlu menampilkan progres download langsung. Kepentingan proses mengalir dari klien ke proses yang menghosting layanan atau penyedia konten yang digunakannya, sehingga arah setiap dependensi menjadi penting:

  • Binding UI ke worker aman. Saat MainActivity mengikat ke AssetDownloadService, sistem akan meningkatkan :bg ke tingkat kepentingan UI yang terlihat. Saat pengguna keluar, proses UI akan di-cache dan binding tidak lagi meningkatkan apa pun.
  • Worker yang bergantung pada proses UI tidak aman. Jika kode di :bg terikat ke layanan atau mengkueri penyedia konten yang berjalan dalam proses utama, sistem akan meningkatkan proses utama ke tingkat kepentingan yang lebih tinggi dari worker, dan memori UI akan tetap disematkan lagi. Minta UI mendaftarkan callback dengan worker, seperti Messenger atau antarmuka callback AIDL. Meneruskan objek binder sebagai callback tidak mengubah tingkat kepentingan salah satu proses.

Batalkan pendaftaran callback sebelum Anda membatalkan binding, sehingga worker tidak pernah memanggil proses UI setelah sistem membekukannya. Contoh berikut mengikat ke worker hanya saat UI terlihat:

Kotlin

class MainActivity : Activity() {
    private var downloadService: IBinder? = null

    private val downloadConnection = object : ServiceConnection {
        override fun onServiceConnected(
                name: ComponentName,
                service: IBinder
        ) {
            downloadService = service
            // Register a Messenger or AIDL callback for progress updates.
        }

        override fun onServiceDisconnected(name: ComponentName) {
            // The binding stays active and reconnects if the worker restarts.
            downloadService = null
        }
    }

    override fun onStart() {
        super.onStart()
        val intent = Intent(this, AssetDownloadService::class.java)
        bindService(intent, downloadConnection, Context.BIND_AUTO_CREATE)
    }

    override fun onStop() {
        super.onStop()
        if (downloadService != null) {
            // Unregister the progress callback here, before unbinding.
            downloadService = null
        }
        // Unbind the ServiceConnection even if onServiceConnected hasn't run
        // yet.
        unbindService(downloadConnection)
    }
}

Java

public final class MainActivity extends Activity {
    private IBinder downloadService;

    private final ServiceConnection downloadConnection =
            new ServiceConnection() {
                @Override
                public void onServiceConnected(
                        ComponentName name, IBinder service) {
                    downloadService = service;
                    // Register a Messenger or AIDL callback for progress
                    // updates.
                }

                @Override
                public void onServiceDisconnected(ComponentName name) {
                    // The binding stays active and reconnects if the worker
                    // restarts.
                    downloadService = null;
                }
            };

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(this, AssetDownloadService.class);
        bindService(intent, downloadConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (downloadService != null) {
            // Unregister the progress callback here, before unbinding.
            downloadService = null;
        }
        // Unbind the ServiceConnection even if onServiceConnected hasn't run
        // yet.
        unbindService(downloadConnection);
    }
}

Untuk mengetahui informasi selengkapnya tentang pengaruh flag binding terhadap oom_score_adj di seluruh proses, lihat Pengikatan layanan dan status proses.

Mengoordinasikan status bersama di disk dengan aman

Karena proses utama dan :bg berjalan di ruang alamat terpisah dengan runtime dan deskriptor file terpisah:

  • SQLite dan Room: Gunakan write-ahead logging (mode jurnal default di Room di sebagian besar perangkat) sehingga kedua proses dapat membaca dan menulis database secara serentak. Jangan panggil enableMultiInstanceInvalidation dari :bg. Room menghosting layanan invalidasinya di proses utama, sehingga worker akan mengikat ke proses utama dan mempertahankan memori UI pada tingkat kepentingan worker yang lebih tinggi. Sebagai gantinya, minta pekerja melaporkan perubahan melalui callback yang didaftarkan UI, dan minta UI mengkueri ulang database saat dimulai.
  • Preferensi: Jangan bagikan SharedPreferences di seluruh proses. Gunakan DataStore dalam kode multiproses atau teruskan status secara eksplisit melalui IPC.
  • File aset: Tulis paket aset yang didownload ke file staging di :bg, panggil fdatasync, lalu posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED) di :bg setelah menulis (seperti yang ditunjukkan dalam Mengembalikan halaman memori ke OS), dan ganti nama file secara atomik ke tempatnya. Biarkan UI mengambil hasilnya saat dimulai dan terikat ke worker, daripada memanggil proses UI yang mungkin dibekukan.

Mengamati efek di shell adb

Anda dapat memverifikasi bahwa pemisahan proses latar depan dan proses latar belakang mengisolasi penggunaan memori yang meningkat dengan memeriksa status proses Activity Manager dan dumpsys meminfo setelah membuka Layar utama. Contoh output di bagian ini berasal dari aplikasi contoh MemoryLab, yang menjalankan alokasi native 64 MB yang sama dalam proses UI-nya di kedua konfigurasi dan menghosting layanan latar depan terpisah dalam proses :remote.

1. Membandingkan status proses dan skor OOM

Mulai workload latar belakang, tekan tombol Layar utama agar Activity berhenti, dan buat kueri Activity Manager:

adb shell dumpsys activity processes com.example.app | grep -E "Proc #|oom:|state:"

Dalam arsitektur proses tunggal, proses utama tetap ditingkatkan meskipun Activity-nya dihentikan. Selama sekitar 15 detik setelah pengguna keluar, Activity Manager mempertahankan proses yang menjalankan layanan latar depan di oom_score_adj = 50 (fg-service-act), lalu menurunkannya ke 200 (fg-service). Output berikut diambil 20 detik setelah menekan Beranda:

    Proc #1: prcp     F/S/FGS  ---NFUATI  t: 0 13794:com.android.memorylab/u0a284 (fg-service)
        oom: max=1001 curRaw=200 setRaw=200 cur=200 set=200
        state: cur=FGS  set=FGS   lastRss=0.00 lastCachedRss=0.00

Dalam arsitektur proses terpisah, hanya proses worker yang tetap berada di oom_score_adj = 200, sedangkan proses UI utama turun ke PREV (oom_score_adj = 700) dan kemudian ke bucket yang di-cache (oom_score_adj >= 900):

    Proc #3: prcp     F/S/FGS  ---NFUATI  t: 0 13544:com.android.memorylab:remote/u0a284 (fg-service)
        oom: max=1001 curRaw=200 setRaw=200 cur=200 set=200
        state: cur=FGS  set=FGS   lastRss=0.00 lastCachedRss=0.00
    Proc #1: prev     b/ /LAST -------TI  t: 0 13312:com.android.memorylab/u0a284 (previous)
        oom: max=1001 curRaw=700 setRaw=700 cur=700 set=700
        state: cur=LAST set=LAST  lastRss=0.00 lastCachedRss=0.00

Setelah waktu tunggu aplikasi sebelumnya berakhir, proses utama akan berpindah ke bucket yang di-cache (cch, oom_score_adj = 900, previous-expired) dan kehilangan semua kemampuan prosesnya (---------), termasuk waktu CPU implisit (I). Pada saat itu, sistem akan membekukannya dan dapat memadatkan atau mengklaimnya kembali kapan saja tanpa mengganggu proses worker.

2. Membandingkan dumpsys meminfo di berbagai proses

Bandingkan ringkasan dumpsys meminfo dari worker yang ditingkatkan dengan proses UI:

adb shell dumpsys meminfo com.example.app:bg
adb shell dumpsys meminfo com.example.app

Contoh output App Summary untuk proses worker MemoryLab di oom_score_adj = 200:

 App Summary
                       Pss(KB)                        Rss(KB)
                        ------                         ------
           Java Heap:      380                          13840
         Native Heap:     1548                           2832
                Code:      132                          55816
               Stack:      200                            208
            Graphics:        0                              0
       Private Other:      496
              System:     1926
             Unknown:                                    4168

           TOTAL PSS:     4682            TOTAL RSS:    76864       TOTAL SWAP PSS:      441

Contoh output App Summary untuk proses UI MemoryLab di oom_score_adj = 700, yang menyimpan alokasi native 64 MB di Private Other:

 App Summary
                       Pss(KB)                        Rss(KB)
                        ------                         ------
           Java Heap:     1316                          19816
         Native Heap:    30408                          31596
                Code:     3148                         115796
               Stack:      656                            664
            Graphics:        0                              0
       Private Other:    67044
              System:    13687
             Unknown:                                   71092

           TOTAL PSS:   116259            TOTAL RSS:   238964       TOTAL SWAP PSS:      259

Proses worker memerlukan PSS kurang dari 5 MB, karena proses ini berbagi sebagian besar kode dan halaman runtime-nya dengan Zygote. Dalam aplikasi yang merender dengan Vulkan atau OpenGL ES, baris Graphics dari proses UI juga menyertakan memori GPU. Contoh ini diambil di perangkat virtual Cuttlefish dengan rendering software, sehingga Graphics adalah 0 di kedua proses.

3. Memeriksa akuntansi cgroup per proses

Setiap proses di bawah UID Anda memiliki direktori cgroup v2 sendiri di dalam cgroup induk untuk aplikasi Anda. Perintah berikut mencantumkan oom_score_adj, memory.current, dan memory.high di seluruh aplikasi untuk setiap proses aplikasi Anda:

adb shell '
  for PID in $(pidof com.example.app com.example.app:bg); do
    CG=/sys/fs/cgroup$(sed -n "s/^0:://p" /proc/$PID/cgroup)
    echo "pid=$PID ($(tr -d "\0" < /proc/$PID/cmdline))" \
      "adj=$(cat /proc/$PID/oom_score_adj)" \
      "memory.current=$(cat $CG/memory.current)" \
      "app.memory.high=$(cat ${CG%/*}/memory.high)"
  done
'

Contoh output untuk MemoryLab setelah menekan Beranda:

pid=15301 (com.android.memorylab) adj=700 memory.current=103260160 app.memory.high=966787072
pid=16518 (com.android.memorylab:remote) adj=200 memory.current=3608576 app.memory.high=966787072

Proses worker dikenai biaya kurang dari 4 MB, sedangkan proses UI dikenai biaya sekitar 98 MB. Setiap proses memiliki memory.current-nya sendiri, tetapi Pembatas Memori menetapkan batasnya pada cgroup induk yang berisi semua proses aplikasi Anda. Kedua baris menampilkan app.memory.high yang sama, yaitu 966.787.072 byte, yang merupakan batas tidak terlihat sebesar 922 MB dalam output berikut.

Untuk melihat batas terlihat dan tidak terlihat yang diterapkan oleh Pembatas Memori pada perangkat, jalankan perintah berikut:

adb shell am memory-limiter status

Contoh output dari perangkat dengan RAM 4 GB, dengan batas tidak terlihat adalah setengah dari batas terlihat:

Memory limiter
  enabled                  limits=true              killing=true             ignored=none
  visible:                 memHigh=1843MB           memMax=2048MB            swapMax=1024MB
  not-visible:             memHigh=922MB            memMax=1024MB            swapMax=1024MB

Mengamati efek di Perfetto

Perfetto memungkinkan Anda memvisualisasikan linimasa oom_score_adj dan RSS di kedua proses saat pengguna berpindah dari UI latar depan ke Layar utama saat pekerjaan latar belakang berlanjut.

1. Merekam aktivitas

Rekam rekaman aktivitas berdurasi 15 detik yang menangkap penghitung memori proses, memori GPU, dan perubahan oom_score_adj saat memulai tugas latar belakang di aplikasi Anda dan membuka Layar utama:

adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/process-splitting.perfetto-trace <<'EOF'
buffers: {
  size_kb: 32768
  fill_policy: DISCARD
}
data_sources: {
  config {
    name: "linux.process_stats"
    process_stats_config {
      scan_all_processes_on_start: true
      proc_stats_poll_ms: 250
    }
  }
}
data_sources: {
  config {
    name: "android.gpu.memory"
  }
}
data_sources: {
  config {
    name: "linux.ftrace"
    ftrace_config {
      ftrace_events: "oom/oom_score_adj_update"
      ftrace_events: "gpu_mem/gpu_mem_total"
    }
  }
}
duration_ms: 15000
EOF
adb pull /data/misc/perfetto-traces/process-splitting.perfetto-trace

2. Memeriksa jalur proses di UI Perfetto

Buka rekaman aktivitas di Perfetto UI dan luaskan grup proses untuk com.example.app dan com.example.app:bg:

  1. Sematkan trek oom_score_adj dan mem.rss.anon untuk kedua proses agar muncul berdampingan di bagian atas linimasa.
  2. Temukan stempel waktu saat pengguna menekan Layar utama:
    • Dalam proses worker, oom_score_adj tetap di 200 dan mem.rss.anon tetap datar dan kecil.
    • Dalam proses UI, oom_score_adj naik dari 0 ke 200, tetap di sana selama sekitar satu detik saat Activity berhenti, lalu naik ke 700 (PREV). Setelah waktu tunggu aplikasi sebelumnya, aplikasi akan berpindah ke rentang yang di-cache, tempat sistem dapat memadatkan dan mengklaim kembali memorinya tanpa memengaruhi worker.
Pelacakan Perfetto untuk proses UI dan proses worker. Proses UI
       oom_score_adj meningkat dari 0 menjadi 200, lalu 700 setelah Home, sementara
       proses worker tetap di 200 dengan RSS anonim sekitar 19 MB.
Gambar 2. Proses terpisah setelah pengguna menekan Beranda.

Pada Gambar 2, proses UI tetap berada di 200 sebentar saat Activity-nya berhenti, lalu naik ke 700, sementara worker tetap berada di 200 dengan RSS anonim sekitar 19 MB. Sebagai perbandingan, Gambar 3 menunjukkan rekaman aktivitas berdurasi 35 detik dari konfigurasi yang ditempatkan bersama, dengan satu proses tetap berada di 50, lalu 200 dengan semua memori UI-nya.

Pelacakan Perfetto untuk satu proses yang berada di lokasi yang sama. oom_score_adj
       naik dari 0 ke 50 setelah Home dan ke 200 sekitar 15 detik kemudian, sementara
       RSS anonim tetap tinggi.
Gambar 3. Proses yang ditempatkan bersama setelah pengguna menekan Beranda.

Untuk menjelajahi tampilan ini, buka rekaman aktivitas terpisah dan rekaman aktivitas yang ditempatkan bersama di UI Perfetto.

3. Membuat kueri jejak memori yang meningkat dengan PerfettoSQL

Jalankan kueri PerfettoSQL berikut di tab Query (SQL) untuk mengukur rata-rata RSS anonim dan file yang disimpan oleh setiap proses aplikasi Anda di setiap level oom_score_adj:

INCLUDE PERFETTO MODULE android.memory.process;

SELECT
  process_name,
  score AS oom_score_adj,
  ROUND(SUM(dur) / 1e9, 2) AS duration_sec,
  ROUND(SUM(anon_rss * dur) / SUM(dur) / 1048576.0, 1) AS avg_anon_mb,
  ROUND(SUM(file_rss * dur) / SUM(dur) / 1048576.0, 1) AS avg_file_mb
FROM memory_oom_score_with_rss_and_swap_per_process
WHERE process_name GLOB 'com.example.app*'
GROUP BY process_name, score
ORDER BY process_name, score;

Contoh output untuk trace terpisah pada Gambar 2:

process_name                  oom_score_adj  duration_sec  avg_anon_mb  avg_file_mb
com.android.memorylab                     0          4.11         93.3        110.4
com.android.memorylab                   200          1.06        126.8        110.5
com.android.memorylab                   700          9.72        115.1        110.5
com.android.memorylab:remote              0          0.01         NULL         NULL
com.android.memorylab:remote            200         12.87         18.9         53.7

Contoh output untuk trace yang ditempatkan bersama pada Gambar 3:

process_name           oom_score_adj  duration_sec  avg_anon_mb  avg_file_mb
com.android.memorylab              0          4.10         96.0        110.4
com.android.memorylab             50         16.04        117.6        110.5
com.android.memorylab            200         14.68        116.5        110.6

Dalam konfigurasi terpisah, hanya sekitar 19 MB memori anonim yang tetap berada di oom_score_adj = 200 setelah detik pertama. Dalam konfigurasi yang ditempatkan bersama, sekitar 117 MB tetap berada di 50, lalu 200 selama layanan latar depan berjalan.


← Mengembalikan halaman memori ke OS | ↑ Naik