Kode yang Anda tulis sendiri merupakan bentuk penggunaan memori. Setiap class, metode, dan konstanta string di aplikasi Anda harus dimuat ke dalam RAM saat dieksekusi. Makin besar basis kode aplikasi Anda, makin banyak memori yang akan digunakan hanya untuk ada.
Memori yang didukung file dan paging sesuai permintaan
Android memuat kode yang dapat dieksekusi dari .apk Anda (seperti file .oat atau .so)
menggunakan mmap. Artinya, kode tersebut didukung file.
Yang penting, Android menggunakan paging sesuai permintaan. Saat aplikasi Anda dimulai, kernel tidak memuat seluruh APK ke dalam RAM secara langsung. Sebagai gantinya, hanya memetakan file ke ruang alamat virtual proses. Saat aplikasi Anda dieksekusi, dan CPU beralih ke fungsi baru, hal ini akan memicu "kesalahan halaman". Kernel menjeda thread, membaca halaman kode 4 KB tertentu dari penyimpanan ke RAM fisik, dan melanjutkan eksekusi.

Artinya, kode yang Anda paketkan tetapi tidak pernah dieksekusi tidak menggunakan memori fisik untuk halaman kode itu sendiri. Namun, library yang tidak digunakan tetap meningkatkan ukuran APK secara keseluruhan dan dapat meningkatkan penggunaan memori secara signifikan oleh metadata internal sistem (seperti indeks DEX dan deskriptor class), yang harus dibaca untuk mengetahui bahwa kode tersebut ada. Selain itu, banyak library berisi penginisialisasi statis atau dipengaruhi oleh framework injeksi dependensi selama startup aplikasi, sehingga library tersebut tetap di-paging ke RAM.
Pengusiran halaman dan pelambatan
Karena memori yang didukung file selalu dapat dibaca ulang dari penyimpanan, kernel menganggap halaman ini "bersih". Saat sistem mengalami tekanan memori, kernel akan mengeluarkan (menghapus) halaman kode bersih ini dari RAM untuk memberi ruang bagi hal lain.
Jika aplikasi Anda kemudian perlu menjalankan kode tersebut lagi, CPU akan mengalami kesalahan, dan kernel harus membaca ulang halaman dari penyimpanan. Makin banyak kode yang dimiliki aplikasi Anda, makin rentan kode tersebut dikeluarkan. Saat pengguna kembali ke aplikasi Anda yang terlalu besar setelah menggunakan aplikasi lain, mereka akan mengalami jank dan pelambatan acak karena CPU terus terhenti menunggu kode dipanggil kembali dari penyimpanan.
Biaya Kesalahan Halaman: Meskipun sangat bervariasi berdasarkan kecepatan penyimpanan perangkat (UFS vs. eMMC) dan status kernel, kesalahan halaman besar (membaca 4 KB dari penyimpanan) dapat berbiaya antara 0,5 md hingga 5 md. Jika jalur startup Anda menyentuh 500 halaman kode yang tidak dioptimalkan, Anda dapat dengan mudah menambahkan latensi I/O murni beberapa ratus milidetik ke waktu peluncuran aplikasi Anda.
Mempelajari ukuran kode dengan Compiler Explorer
Untuk membangun intuisi tentang cara kode Java atau Kotlin Anda diterjemahkan ke kode mesin native (dan dengan demikian byte memori), Anda dapat menggunakan Compiler Explorer.
Dukungan Android dibuat langsung ke dalam Godbolt. Dengan ini, Anda dapat melihat cara berbagai bagian toolchain Android (D8, R8, dan dex2oat) mentransformasi kode sumber Anda.
Cara menggunakan Compiler Explorer dengan Android
- Buka godbolt.org.
- Pilih Android Java atau Android Kotlin dari dropdown bahasa (kiri atas).
- Di dropdown compiler (kanan atas panel kode), Anda dapat memilih
berbagai alat:
d8: Menampilkan bytecode Dalvik (.dex). Ini adalah representasi terdekat dengan kode asli Anda dan lebih mudah dibaca.r8: Menunjukkan cara pengoptimal R8 menyusutkan dan mengoptimalkan bytecode Anda.dex2oat: Menampilkan kode mesin ARM64 akhir yang benar-benar dieksekusi di perangkat. Di sinilah Anda dapat melihat dampak memori yang sebenarnya (4 byte per petunjuk).dex2oatdapat menargetkan ISA yang berbeda, tetapi ARM64 adalah yang paling umum untuk ponsel.
- Penyorotan Input<>Output: Mengarahkan kursor ke baris kode akan menandai petunjuk bytecode atau kode mesin yang sesuai, sehingga memudahkan untuk melacak dampak pernyataan tertentu.
- Pipeline Pengoptimalan: Dalam tampilan pembongkaran, Anda dapat mengklik Tambahkan yang baru... -> Opt Pipeline. Hal ini memungkinkan Anda melihat langkah-langkah internal yang dilakukan compiler. Anda dapat memeriksa cara Representasi Internal (IR) diubah pada setiap tahap (misalnya, antara langkah "Inliner (sebelum)" dan "Inliner (setelah)") sebelum diturunkan ke kode mesin ARM64 akhir.

Mengapa hal ini penting untuk memori
Setiap instruksi yang Anda lihat dalam output dex2oat yang menargetkan ARM64 ISA akan menggunakan 4 byte dalam file yang dapat dieksekusi (.odex atau .oat) aplikasi Anda.
Coba masukkan kode yang menggunakan fitur bahasa yang berbeda dan pelajari output compiler:
- Akses Array vs. Iterator Daftar:
- Loop array sederhana pada
int[]dapat dikompilasi menjadi ~10 petunjuk (~40 byte). - Loop foreach pada
Listsecara implisit menggunakanIterator. Hal ini dapat menghasilkan 30-40 instruksi (~160 byte) karena panggilan metode tambahan (hasNext(),next()) dan alokasi objek iterator itu sendiri. - Pengoptimalan R8: Dalam kondisi yang tepat (misalnya, saat
Listterbukti merupakanArrayList), pengoptimal R8 dapat mengubah loop foreach kembali menjadi loop berindeks sederhana, sehingga menghilangkan overhead iterator dan mengurangi ukuran kode serta churn memori runtime.
- Loop array sederhana pada
- Panggilan Metode Virtual: Melibatkan pemuatan class objek, menemukan
metode di
vtable, lalu melakukan percabangan. Proses ini biasanya memerlukan 4-5 petunjuk (~20 byte). - Panggilan Langsung/Statis: Sering kali diterjemahkan ke dalam satu instruksi
bl(Cabang dengan Link) (4 byte). - Lambda Kotlin: Dapat menghasilkan seluruh class anonim dan metode jembatan tambahan, yang menambahkan ratusan byte overhead kode dan metadata untuk blok fungsional sederhana.
Dengan menggunakan Compiler Explorer, Anda dapat melihat bagaimana fitur bahasa yang canggih (seperti lambda Kotlin, API stream, atau penggunaan generik yang berat) memengaruhi ukuran aplikasi yang dikompilasi akhir, dan bagaimana pengoptimal seperti R8 dapat mengatasi biaya abstraksi bahasa dalam beberapa kasus. Alat ini dapat membantu Anda membuat pertimbangan yang tepat dalam merancang dan menerapkan aplikasi.
Secara umum, semakin kompleks kode aplikasi Anda, semakin tinggi penggunaan memori. Sebaliknya, kode yang lebih sederhana - atau kode yang disederhanakan oleh R8 - menghasilkan representasi yang lebih kecil sebagai instruksi CPU dan byte dalam penyimpanan dan RAM.
Mengukur dampak kode dengan meminfo dan showmap
Anda dapat menggunakan alat memori Android standar untuk melihat seberapa banyak memori yang digunakan oleh kode aplikasi Anda.
dumpsys meminfo
Saat Anda menjalankan adb shell dumpsys meminfo <package>, kategori Code di bagian
Ringkasan Aplikasi memberikan tampilan tingkat tinggi tentang memori terkait kode:
App Summary
Pss(KB)
------
Java Heap: 3244
Native Heap: 5412
Code: 24512 # <--- Sum of .so, .dex, .oat, .art, etc.
showmap
Untuk tampilan yang lebih terperinci, gunakan showmap. Hal ini mengungkapkan region di luar file tertentu yang dipetakan ke memori.
adb shell showmap $(pidof <package>) | grep -E "\.oat|\.odex|\.dex|\.apk"
Anda akan melihat entri untuk kode yang dikompilasi aplikasi Anda:
size RSS PSS clean dirty clean dirty swap swapPSS object
------- -------- -------- -------- -------- -------- -------- -------- -------- ----------------
12288 8192 8192 8192 0 0 0 0 0 /data/app/.../base.odex
Kode tidak terpakai dan R8
Karena setiap metode yang dijalankan menggunakan memori, aplikasi yang "membengkak" dengan inisialisasi yang tidak perlu atau library yang tidak digunakan dapat sangat memengaruhi performa startup dan penggunaan memori dasar.
Itulah sebabnya alat seperti R8 (ProGuard) sangat penting. R8 menganalisis bytecode aplikasi Anda dan menghapus class atau metode yang tidak pernah dipanggil ("penghapusan kode tidak terpakai").
Latihan interaktif: biaya bloat
Untuk menunjukkan dampak ukuran kode, pertimbangkan eksperimen yang membandingkan dua build aplikasi yang berisi 300 class yang dihasilkan (masing-masing dengan 500 metode):
- CodeBloat (Tidak Dioptimalkan): Build standar yang tidak dioptimalkan dan berisi semua class yang dihasilkan dan string unik.
- CodeBloatOptimized: Kode sumber yang sama, tetapi dikompilasi dengan penyingkatan R8 diaktifkan.
1. Kompilasi ahead-of-time (AOT)
Untuk memaksimalkan dampak memori yang didukung file, kami akan menggunakan alat cmd package compile
untuk mengompilasi aplikasi ke dalam file .oat dengan kompilasi ahead-of-time (AOT).
adb shell cmd package compile -m speed -f com.android.codebloat
adb shell cmd package compile -m speed -f com.android.codebloat.optimized
Perhatikan bahwa ini adalah contoh sintetis. Biasanya, aplikasi akan menggunakan mode kompilasi
speed-profile (lihat lebih lanjut di bawah).
2. Luncurkan dan bandingkan
Untuk melihat mulai benar-benar dingin saat sistem harus membaca kode dari penyimpanan, kita akan menghapus cache halaman kernel sebelum meluncurkan setiap aplikasi. Hal ini memerlukan akses root.
Luncurkan aplikasi yang tidak dioptimalkan:
adb shell am force-stop com.android.codebloat
# Drop page cache to ensure the start is truly cold
adb shell "echo 3 > /proc/sys/vm/drop_caches"
adb shell am start -W -n com.android.codebloat/.MainActivity
sleep 5 # Wait for the background thread to load classes
adb shell dumpsys meminfo -s com.android.codebloat
Sekarang lakukan hal yang sama untuk aplikasi yang dioptimalkan:
adb shell am force-stop com.android.codebloat.optimized
# Drop page cache to ensure the start is truly cold
adb shell "echo 3 > /proc/sys/vm/drop_caches"
adb shell am start -W -n com.android.codebloat.optimized/com.android.codebloat.MainActivity
sleep 5
adb shell dumpsys meminfo -s com.android.codebloat.optimized
Hasil
Jika melihat baris Code di bagian App Summary, Anda akan melihat perbedaan yang sangat besar:
- Tidak dioptimalkan
Code: ~30.000 KB (30 MB) - Dioptimalkan
Code: ~2.000 KB (2 MB)
Karena R8 menentukan bahwa 500 metode di dalam class tersebut tidak pernah melakukan sesuatu yang berguna (metode doSomething() hanya memanggil method0(), dan hasilnya diabaikan), R8 menghapus hampir semua kode yang dibuat secara artifisial dari APK akhir.
3. Melihat dampak di Perfetto
Dampak pembengkakan kode terlihat jelas selama fase pemuatan awal
aplikasi. Secara khusus, cari slice bindApplication di
thread utama, dan slice bertingkat yang dimulai dengan madvising, yang menunjukkan
sistem sedang bersiap untuk memuat file dari APK dan kode yang dikompilasinya (.odex).
Saat cold start interaktif, sistem akan memuat kode mmap() dan madvise() serta data lain dari file ini yang diperlukan agar aplikasi dapat dimuat dan dijalankan. Nilai setelah "size=" dalam irisan madvising menunjukkan jumlah data yang perlu dimuat. Pengambilan data kode aplikasi ini dilakukan untuk mempercepat startup
aplikasi.
Dari perbandingan tersebut, kita dapat melihat bahwa jumlah kode aplikasi yang perlu dimuat dari penyimpanan ke RAM jauh lebih besar dalam kasus aplikasi yang membengkak, sehingga durasi yang lebih lama berkontribusi pada peluncuran aplikasi yang lebih lambat. Selain itu, rekaman aktivitas saat aplikasi yang terlalu besar dimulai menunjukkan irisan untuk memuat file DEX sekunder (classes2.dex, classes3.dex) yang terpaksa "tumpah" ke dalam aplikasi yang terlalu besar karena tidak muat dalam satu file DEX.
Sebagai perbandingan (cold start di Pixel 10a)
| Metrik | Tidak dioptimalkan (CodeBloat) | Dioptimalkan (CodeBloatOptimized) |
|---|---|---|
base.odex madvise size |
~7,9 MB (2,0 md) | ~16 KB (0,003 md) |
base.apk madvise size |
~2,4 MB (2,4 md) | ~4 KB (0,001 md) |
classes2.dex madvise size |
~7,3 MB (8,6 md) | T/A |
classes3.dex madvise size |
~7,3 MB (8,0 md) | T/A |
Total durasi madvising |
~21 md | ~0,004 md |
Performa pemuatan aplikasi yang tidak dioptimalkan

Performa pemuatan aplikasi yang dioptimalkan

Dampak pembengkakan kode bervariasi berdasarkan ukuran aplikasi, karakteristik perangkat pengguna, dan beban sistem.
PerfettoSQL untuk analisis pemuatan
Anda dapat menggunakan kueri berikut untuk mengekstrak metrik ini dari rekaman aktivitas Anda.
1. Durasi peluncuran aplikasi
Ini menunjukkan waktu sejak Aktivitas aplikasi diluncurkan hingga Aktivitas telah menggambar frame pertama.
INCLUDE PERFETTO MODULE android.startup.startups;
SELECT package, dur, startup_type
FROM android_startups
WHERE package LIKE 'com.android.codebloat%';
Lihat: Memahami berbagai status peluncuran aplikasi
Durasi startup aplikasi sensitif terhadap banyak faktor selain yang dibahas dalam panduan ini.
2. Mengekstrak ukuran dan durasi madvising
Kueri ini memperbesar bagian madvising yang kita lihat di atas.
INCLUDE PERFETTO MODULE slices.with_context;
SELECT
name,
dur/1e6 AS dur_ms
FROM thread_slice
WHERE process_name LIKE 'com.android.codebloat%'
AND name LIKE 'madvising %';
3. Pengelompokan status thread utama (total durasi per status)
Kueri ini menunjukkan berapa banyak waktu yang dihabiskan thread utama aplikasi dalam berbagai status.
SELECT
p.name AS process_name,
state,
sum(dur)/1e6 AS total_dur_ms
FROM thread_state ts
JOIN thread t USING (utid)
JOIN process p USING (upid)
WHERE p.name LIKE 'com.android.codebloat%'
AND t.is_main_thread = 1
GROUP BY p.name, state;
Anda dapat menyempurnakan kueri untuk hanya melihat status thread utama selama durasi peluncuran aplikasi.
INCLUDE PERFETTO MODULE android.startup.startups;
SELECT
p.name AS process_name,
ts.state,
-- Calculate only the duration that falls within the startup window
SUM(
MAX(0,
MIN(ts.ts + ts.dur, s.ts + s.dur) - MAX(ts.ts, s.ts)
)
) / 1e6 AS startup_dur_ms
FROM thread_state ts
JOIN thread t USING (utid)
JOIN process p USING (upid)
-- Join on the package name to align thread states with the correct startup
JOIN android_startups s ON s.package = p.name
WHERE p.name LIKE 'com.android.codebloat%'
AND t.is_main_thread = 1
-- Only select thread states that overlap with the startup interval
AND ts.ts + ts.dur > s.ts
AND ts.ts < s.ts + s.dur
GROUP BY 1, 2
ORDER BY startup_dur_ms DESC;
Hal ini dapat memaparkan beberapa masalah menarik, misalnya:
- Waktu yang dihabiskan untuk Runnable (R) tinggi, tetapi tidak Berjalan: Hal ini menunjukkan bahwa startup aplikasi tertunda oleh pertentangan CPU, yaitu thread utama aplikasi tidak dapat berjalan karena thread lain (mungkin dari aplikasi lain) sedang menggunakan CPU.
- Waktu yang dihabiskan dalam Tidur yang Dapat Diinterupsi (D) tinggi: Hal ini biasanya menunjukkan I/O lambat atau tekanan memori yang menghentikan peluncuran aplikasi.
- Waktu yang dihabiskan untuk Tidur (S) tinggi: Artinya, thread utama menunggu thread lain melakukan pekerjaan. Terkadang hal ini menunjukkan pertentangan penguncian di jalur startup aplikasi (yaitu, thread utama diblokir pada resource eksklusif yang digunakan oleh thread lain di aplikasi).
4. Memori yang didukung file maksimum (file RSS)
Metrik ini berkorelasi baik dengan jumlah kode dan data yang dimuat aplikasi saat startup. Aplikasi yang lebih "membengkak" akan mencapai angka yang lebih tinggi di sini, sehingga menyebabkan tekanan memori pada sistem. Tekanan tersebut pada gilirannya dapat menunda startup aplikasi, karena sistem berjuang untuk memenuhi permintaan alokasi, atau mengalihkan waktu CPU dari berfokus pada memulai aplikasi dan ke arah merebut kembali memori dari proses lain untuk memenuhi kebutuhan langsung aplikasi yang dimulai.
SELECT
p.name AS process_name,
max(c.value)/1024.0/1024.0 AS max_rss_file_mb
FROM counter c
JOIN process_counter_track t ON c.track_id = t.id
JOIN process p USING (upid)
WHERE p.name LIKE 'com.android.codebloat%'
AND t.name = 'mem.rss.file'
GROUP BY p.name;
Mode kompilasi dan memori ART
Android Runtime (ART) dapat mengompilasi kode aplikasi Anda dalam salah satu dari beberapa mode yang berbeda, yang juga dikenal sebagai filter compiler. Filter compiler yang dipilih berdampak langsung pada jejak memori aplikasi Anda.
verify: ART hanya melakukan verifikasi bytecode. Tidak ada kompilasi AOT yang dilakukan. Kode dieksekusi melalui Interpreter atau dikompilasi saat runtime oleh compiler JIT.- Dampak Memori: Ukuran di disk terkecil. Penggunaan memori kode native didorong ke
JIT Cache(memori kotor anonim).
- Dampak Memori: Ukuran di disk terkecil. Penggunaan memori kode native didorong ke
speed: ART melakukan kompilasi AOT penuh dari semua metode.- Dampak Memori: Ukuran
.odexterbesar. Memaksimalkan penggunaan memori yang didukung file (bersih).
- Dampak Memori: Ukuran
speed-profile: ART hanya mengompilasi metode yang telah ditandai sebagai "hot" dalam profil JIT.- Dampak Memori: Pendekatan seimbang. Hanya kode paling penting yang dikompilasi AOT.
Filter yang paling umum adalah speed-profile, yang digunakan saat menginstal aplikasi pengguna. Hal ini dikonfigurasi di properti sistem pm.dexopt.install dan
pm.dexopt.bg-dexopt, dan biasanya ditetapkan di
build/make/target/product/runtime_libart.mk.
Beberapa aplikasi sistem akan menggunakan kompilasi speed, dan juga akan dikompilasi pada waktu build image sistem. verify biasanya hanya digunakan dalam kasus penggunaan pengembangan.
| Kasus penggunaan | Filter compiler umum |
|---|---|
| Pengembangan | verify |
| Image sistem | speed |
| Aplikasi pengguna | speed-profile |
Latihan interaktif: mode kompilasi dan memori
Kita dapat menggunakan aplikasi CodeBloat untuk melihat pengaruh filter ini terhadap memori. Untuk
mereproduksi pengukuran ini:
- Memaksa kompilasi ulang aplikasi ke mode target.
- Hentikan secara paksa dan mulai ulang aplikasi.
- Tunggu hingga thread latar belakang selesai menyentuh class (lihat logcat atau tunggu 5 detik).
- Jalankan
adb shell dumpsys meminfo com.android.codebloat.
Mode: verify (tanpa AOT)
adb shell cmd package compile -m verify -f com.android.codebloat
adb shell am force-stop com.android.codebloat
adb shell am start -W -n com.android.codebloat/.MainActivity
sleep 5
adb shell dumpsys meminfo com.android.codebloat
Dalam mode verify, Ringkasan aplikasi menampilkan: * PSS Kode: ~8.000 KB * Dalvik
Lainnya (JIT): ~25.000 KB
Karena tidak ada kode yang dikompilasi AOT, runtime harus mengompilasi metode aktif JIT ke dalam
Cache JIT, yang muncul sebagai memori anonim kotor (Dalvik
Other).
Mode: speed (AOT penuh)
adb shell cmd package compile -m speed -f com.android.codebloat
adb shell am force-stop com.android.codebloat
adb shell am start -W -n com.android.codebloat/.MainActivity
sleep 5
adb shell dumpsys meminfo com.android.codebloat
Dalam mode speed, hasilnya berubah secara drastis: * PSS Kode: ~24.000 KB *
Dalvik Lainnya (JIT): ~5.000 KB
Kode aplikasi kini dipetakan dari file .odex sebagai memori yang didukung file bersih. Hal ini mengurangi tekanan pada cache JIT dan membuat
memori memenuhi syarat untuk dikeluarkan saat ada tekanan, bukan "macet" sebagai RAM
kotor.
Mode: speed-profile (AOT selektif)
Aplikasi modern dapat memaketkan profil dasar pengukuran baseline.prof. ART menggunakan ini untuk
mengompilasi secara selektif hanya kode yang diperlukan untuk pengaktifan yang cepat dan hemat memori.
Dalam latihan ini, kita akan membuat profil dasar pengukuran untuk mencantumkan class startup aplikasi. Namun, pada kenyataannya, compiler juga dapat menerima profil dari sumber eksternal seperti dari toko aplikasi ("profil cloud"), yang dapat menyediakan profil JIT yang diperoleh dari banyak sumber untuk aplikasi, terlepas dari apakah developer juga memaketkan profil dasar yang mereka buat.
Membuat dan menggunakan profil di perangkat
Untuk melihat dampak speed-profile, Anda dapat membuat profil Anda sendiri di perangkat:
Reset dan Mulai:
adb shell am force-stop com.android.codebloatBerinteraksi: Mulai aplikasi dan biarkan aplikasi menjalankan urutan startup-nya.
Dump Profile:
adb shell kill -s SIGUSR1 $(pidof com.android.codebloat)(Tindakan ini akan memaksa aplikasi untuk menulis profilnya saat ini ke disk).
Install Profile:
adb shell cp /data/misc/profiles/cur/0/com.android.codebloat/primary.prof \ /data/misc/profiles/ref/com.android.codebloat/primary.profKompilasi:
adb shell cmd package compile -m speed-profile -f com.android.codebloat
Saat meluncurkan lagi, Anda akan melihat keseimbangan: PSS Kode akan lebih rendah daripada
speed (misalnya, ~16.000 KB) karena hanya metode peluncuran "hot" yang dikompilasi,
sehingga sisanya ditangani oleh interpreter atau JIT hanya jika benar-benar
digunakan.
Lihat:
- Ringkasan Profil Dasar Pengukuran
- Perbedaan antara Profil Dasar Pengukuran dan Profil Sistem Dimulai
Mempelajari kode yang dikompilasi secara mendalam
Jika Anda ingin melihat dengan tepat petunjuk yang dihasilkan ART, lihat
art/DISASSEMBLY_GUIDE.md.
Panduan ini memberikan petunjuk mendetail tentang cara menggunakan:
oatdump: Untuk melihat petunjuk ARM64 di dalam file.odexyang ada.dex2oat: Untuk menyimulasikan kompilasi dengan tanda debug verbose.
Latihan: penyisipan kode
Salah satu alasan kode yang dikompilasi dapat bertambah secara tidak terduga adalah penyisipan metode. Compiler dapat memutuskan untuk menyalin isi metode kecil yang sering dipanggil langsung ke pemanggilnya.
Di aplikasi CodeBloat, metode doSomething() di setiap class yang dihasilkan
hanya memanggil method0(). Saat dikompilasi dalam mode speed, compiler Pengoptimalan ART kemungkinan akan menyisipkan method0() ke dalam doSomething().
Latihan: Verifikasi ini menggunakan oatdump di perangkat Anda:
# 1. Find the path to the application's APK and compiled .odex file
adb shell pm path com.android.codebloat
# Output: package:/data/app/~~.../base.apk
adb shell "dumpsys package com.android.codebloat | grep 'location is' | head -n 1"
# Example output: [location is /data/app/~~.../oat/arm64/base.odex]
# 2. Run oatdump (substituting the correct path to base.odex)
adb shell oatdump --oat-file=/data/app/~~.../oat/arm64/base.odex \
--class-filter=com.android.codebloat.GeneratedClass0
Cari metode doSomething di output. Jika di-inline, Anda akan melihat petunjuk untuk memuat konstanta string panjang secara langsung dalam doSomething, bukan petunjuk bl yang menargetkan method0.
Memvisualisasikan pengoptimalan (CFG)
Untuk melihat secara tepat kapan compiler memutuskan untuk menyisipkan metode, Anda dapat membuat Grafik Alur Kontrol (CFG). Bagian ini menunjukkan status kode di setiap tahap pipeline pengoptimalan, dengan setiap transformasi melalui Representasi Menengah (IR) compiler hingga kode diturunkan ke ISA target (misalnya, ARM64).
Jalankan
dex2oatdengan tanda dump: Gunakan tanda--verbose-methodsuntuk membatasi output ke metode tertentu; jika tidak, file.cfguntuk aplikasi besar dapat bertambah hingga beberapa gigabita.# Substitution of actual paths required: adb shell dex2oat64 --dex-file=/data/app/~~.../base.apk \ --oat-file=/data/local/tmp/dump.odex \ --compiler-filter=speed \ --dump-cfg=/data/local/tmp/codebloat.cfg \ --verbose-methods=doSomethingTarik dan Lihat: Tarik file
.cfgke workstation Anda dan buka dengan IR Hydra.Menemukan Inliner: Di IR Hydra, muat artefak kompilasi dan telusuri
doSomething. Bandingkan representasi sebelum dan setelah penerusan Inliner. Anda akan melihat grafik meluas saat petunjuk darimethod0digabungkan ke pemanggil.
Atau, gunakan alat Opt Pipeline di Compiler Explorer (seperti yang dijelaskan di bagian atas) dan masukkan kode serupa untuk melihat transformasi serupa yang dilakukan pada pass Inliner.
Latihan: kolom volatile dan penghalang memori
Di aplikasi MemoryLab, kolom mGarbageSink ditandai sebagai volatile. Hal ini
memastikan bahwa compiler tidak mengoptimalkan alokasi sampah kita.
public volatile byte[] mGarbageSink;
Dalam disassembly ARM64, Anda akan melihat bahwa setiap penyimpanan ke kolom ini
disertai dengan Penghalang Memori (dmb ish) atau penggunaan
instruksi Load-Acquire/Store-Release (ldar/stlr). Hal ini memastikan visibilitas
thread, tetapi menambahkan beberapa instruksi tambahan ke setiap akses, sehingga
ukuran kode sedikit meningkat dibandingkan dengan kolom biasa.
Latihan: Temukan akses kolom dan penghalang memori terkait dalam disassembly.
Latihan: pemeriksaan penangguhan implisit
Jika Anda membongkar loop, seperti yang ada di generateAllocationChurn, Anda akan
melihat petunjuk aneh di akhir isi loop:
ldr x21, [x21]
Ini adalah Pemeriksaan Penangguhan Implisit. ART menggunakannya untuk memungkinkan Pengumpul Sampah menjeda thread dengan aman. Register x21 biasanya mengarah ke dirinya sendiri.
Saat GC perlu menangguhkan thread, GC akan "meracuni" lokasi memori tersebut. Lain kali saat thread menjalankan ldr tersebut, thread akan memicu kesalahan, yang ditangkap oleh runtime dan digunakan untuk mentransisikan thread ke status ditangguhkan.
Pola ini diulang di setiap loop dan di awal setiap metode, sehingga berkontribusi pada ukuran kode total aplikasi Anda.
Latihan: Temukan semua pemeriksaan penangguhan implisit dalam disassembly metode, dan coba korelasikan dengan kode sumber asli.
← WebView | ↑ Atas | Threads →