Saya menghabiskan 4 hari penuh mencoba semua yang saya bisa untuk mengetahui kebocoran memori dalam aplikasi yang saya kembangkan, tetapi hal-hal sudah lama tidak masuk akal.
Aplikasi yang saya kembangkan bersifat sosial, jadi pikirkan Profil Aktivitas (P) dan daftar Aktivitas dengan data - misalnya lencana (B). Anda dapat melompat dari profil ke daftar lencana ke profil lain, ke daftar lain, dll.
Jadi bayangkan aliran seperti ini P1 -> B1 -> P2 -> B2 -> P3 -> B3, dll. Untuk konsistensi, saya memuat profil dan lencana dari pengguna yang sama, jadi setiap halaman P sama dan begitu juga setiap halaman B.
Inti umum dari masalahnya adalah: setelah menavigasi sebentar, tergantung pada ukuran setiap halaman, saya mendapatkan pengecualian kehabisan memori di tempat acak - Bitmap, String, dll - tampaknya tidak konsisten.
Setelah melakukan semua yang bisa dibayangkan untuk mencari tahu mengapa saya kehabisan ingatan, saya tidak menemukan apa-apa. Yang tidak saya mengerti adalah mengapa Android tidak mematikan P1, B1, dll jika kehabisan memori saat memuat dan malah macet. Saya berharap aktivitas sebelumnya ini mati dan dibangkitkan jika saya pernah Kembali ke mereka melalui onCreate () dan onRestoreInstanceState ().
Jangankan ini - bahkan jika saya melakukan P1 -> B1 -> Kembali -> B1 -> Kembali -> B1, saya masih mengalami crash. Ini menunjukkan semacam kebocoran memori, namun bahkan setelah membuang hprof dan menggunakan MAT dan JProfiler, saya tidak dapat menunjukkannya.
Saya telah menonaktifkan pemuatan gambar dari web (dan meningkatkan data pengujian yang dimuat untuk menebusnya dan membuat pengujian adil) dan memastikan cache gambar menggunakan SoftReferences. Android sebenarnya mencoba membebaskan beberapa SoftReferences yang dimilikinya, tetapi tepat sebelum memori mogok.
Halaman lencana mendapatkan data dari web, memuatnya ke dalam larik EntityData dari BaseAdapter dan memasukkannya ke ListView (Saya sebenarnya menggunakan MergeAdapter yang sangat baik dari CommonsWare , tetapi dalam aktivitas Lencana ini, sebenarnya hanya ada 1 adaptor, tapi saya ingin menyebutkan fakta ini dengan cara apa pun).
Saya telah memeriksa kode dan tidak dapat menemukan apa pun yang akan bocor. Saya membersihkan dan membatalkan semua yang dapat saya temukan dan bahkan System.gc () kiri dan kanan tetapi aplikasi tetap macet.
Saya masih tidak mengerti mengapa aktivitas tidak aktif yang ada di tumpukan tidak menuai, dan saya sangat ingin mengetahuinya.
Pada titik ini, saya sedang mencari petunjuk, saran, solusi ... apa pun yang dapat membantu.
Terima kasih.
sumber
outOfMemory
kesalahan. Terima kasih!Jawaban:
Ini bukan cara kerja. Satu-satunya manajemen memori yang memengaruhi siklus hidup aktivitas adalah memori global di semua proses, karena Android memutuskan bahwa memori hampir habis sehingga perlu menghentikan proses latar belakang untuk mendapatkannya kembali.
Jika aplikasi Anda duduk di latar depan memulai lebih banyak aktivitas, itu tidak akan pernah pergi ke latar belakang, sehingga itu akan selalu mencapai batas memori proses lokalnya sebelum sistem hampir menghentikan prosesnya. (Dan ketika itu benar-benar menghentikan prosesnya, itu akan mematikan proses yang menghosting semua aktivitas, termasuk apa pun yang saat ini ada di latar depan.)
Jadi menurut saya masalah dasar Anda adalah: Anda membiarkan terlalu banyak aktivitas berjalan pada waktu yang sama, dan / atau masing-masing aktivitas tersebut bergantung pada terlalu banyak sumber daya.
Anda hanya perlu mendesain ulang navigasi Anda untuk tidak bergantung pada penumpukan sembarang jumlah aktivitas yang berpotensi berat. Kecuali jika Anda melakukan banyak hal di onStop () (seperti memanggil setContentView () untuk membersihkan hierarki tampilan aktivitas dan menghapus variabel apa pun yang mungkin dipegangnya), Anda hanya akan kehabisan memori.
Anda mungkin ingin mempertimbangkan menggunakan Fragment API baru untuk mengganti tumpukan aktivitas arbitrer ini dengan satu aktivitas yang mengelola memorinya dengan lebih ketat. Misalnya, jika Anda menggunakan fasilitas back-stack dari fragmen, ketika sebuah fragmen berada di back-stack dan tidak lagi terlihat, metode onDestroyView () -nya dipanggil untuk sepenuhnya menghapus hierarki tampilannya, sehingga sangat mengurangi footprint-nya.
Sekarang, sejauh Anda menabrak arus di mana Anda menekan kembali, pergi ke aktivitas, menekan kembali, pergi ke aktivitas lain, dll dan tidak pernah memiliki tumpukan yang dalam, maka ya Anda hanya mengalami kebocoran. Entri blog ini menjelaskan cara men-debug kebocoran: http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html
sumber
Activity
JavaDocs.Beberapa tips:
Pastikan Anda tidak membocorkan konteks aktivitas.
Pastikan Anda tidak menyimpan referensi di bitmap. Bersihkan semua ImageView Anda di Activity # onStop, seperti ini:
Drawable d = imageView.getDrawable(); if (d != null) d.setCallback(null); imageView.setImageDrawable(null); imageView.setBackgroundDrawable(null);
Daur ulang bitmap jika Anda tidak membutuhkannya lagi.
Jika Anda menggunakan cache memori, seperti memory-lru, pastikan tidak menggunakan banyak memori.
Tidak hanya gambar yang memakan banyak memori, pastikan Anda tidak menyimpan terlalu banyak data lain di memori. Ini dapat terjadi dengan mudah jika Anda memiliki daftar yang tidak terbatas di aplikasi Anda. Coba cache data di DataBase.
Di android 4.2, terdapat bug (stackoverflow # 13754876) dengan akselerasi hardware, jadi jika Anda menggunakannya
hardwareAccelerated=true
dalam manifes, memori akan bocor.GLES20DisplayList
- tetap simpan referensi, meskipun Anda melakukan langkah (2) dan tidak ada orang lain yang merujuk ke bitmap ini. Di sini Anda membutuhkan:a) nonaktifkan akselerasi perangkat keras untuk api 16/17;
atau
b) melepaskan tampilan yang menahan bitmap
Untuk Android 3+, Anda dapat mencoba menggunakan
android:largeHeap="true"
diAndroidManifest
. Tetapi itu tidak akan menyelesaikan masalah memori Anda, tunda saja.Jika Anda membutuhkan, seperti, navigasi tak terbatas, maka Fragmen - harus menjadi pilihan Anda. Jadi, Anda akan memiliki 1 aktivitas, yang hanya akan beralih di antara fragmen. Dengan cara ini Anda juga akan menyelesaikan beberapa masalah memori, seperti nomor 4.
Gunakan Memory Analyzer untuk mengetahui penyebab kebocoran memori Anda.
Berikut adalah video yang sangat bagus dari Google I / O 2011: Manajemen memori untuk Aplikasi Android
Jika Anda berurusan dengan bitmap, ini harus dibaca: Menampilkan Bitmaps dengan Efisien
sumber
outOfMemory
kesalahan. Terima kasih!Bitmap sering menjadi penyebab kesalahan memori di Android, jadi itu akan menjadi area yang bagus untuk diperiksa ulang.
sumber
outOfMemory
kesalahan. Terima kasih!Apakah Anda memegang beberapa referensi untuk setiap Kegiatan? AFAIK ini adalah alasan yang mencegah Android menghapus aktivitas dari tumpukan.
Kami Anda dapat mereproduksi kesalahan ini di perangkat lain juga? Saya telah mengalami beberapa perilaku aneh dari beberapa perangkat android tergantung pada ROM dan / atau pabrikan perangkat kerasnya.
sumber
Saya pikir masalahnya mungkin kombinasi dari banyak faktor yang dinyatakan di sini dalam jawaban adalah apa yang memberi Anda masalah. Seperti yang dikatakan @Tim, referensi (statis) ke aktivitas atau elemen dalam aktivitas itu dapat menyebabkan GC melewati Aktivitas. Sini artikel yang membahas aspek ini. Menurut saya, kemungkinan besar masalah berasal dari sesuatu yang membuat Aktivitas dalam status "Proses Terlihat" atau lebih tinggi, yang akan cukup menjamin bahwa Aktivitas dan sumber daya terkaitnya tidak akan pernah diklaim kembali.
Saya mengalami masalah yang berlawanan beberapa waktu yang lalu dengan Layanan, jadi itulah yang membuat saya berpikir ini: ada sesuatu yang membuat Aktivitas Anda tetap tinggi pada daftar prioritas proses sehingga tidak akan tunduk pada GC sistem, seperti referensi (@Tim) atau loop (@Alvaro). Loop tidak perlu menjadi item yang tidak ada habisnya atau berjalan lama, hanya sesuatu yang berjalan seperti metode rekursif atau loop berjenjang (atau sesuatu di sepanjang garis itu).
EDIT: Seperti yang saya pahami, onPause dan onStop dipanggil sesuai kebutuhan secara otomatis oleh Android. Metode yang ada di sana terutama untuk Anda timpa sehingga Anda dapat mengurus apa yang Anda perlukan sebelum proses hosting dihentikan (menyimpan variabel, menyimpan status secara manual, dll.); tetapi perhatikan bahwa dengan jelas dinyatakan bahwa onStop (bersama dengan onDestroy) tidak dapat dipanggil di setiap kasus. Selain itu, jika proses hosting juga menghosting Aktivitas, Layanan, dll. Yang memiliki status "Forground" atau "Visible", OS mungkin tidak akan menghentikan proses / utas. Misalnya: Aktivitas dan Layanan keduanya ditandai dalam proses yang sama dan Layanan kembali
START_STICKY
darionStartCommand()
proses tersebut secara otomatis membutuhkan setidaknya satu status yang terlihat. Itu mungkin kuncinya di sini, coba deklarasikan proc baru untuk Aktivitas dan lihat apakah itu mengubah sesuatu. Coba tambahkan baris ini ke deklarasi Aktivitas Anda di Manifes sebagai:android:process=":proc2"
lalu jalankan pengujian lagi jika Aktivitas Anda berbagi proses dengan hal lain. Pemikirannya di sini adalah bahwa jika Anda telah membersihkan Aktivitas Anda dan cukup yakin bahwa masalahnya bukan Aktivitas Anda, maka sesuatu yang lain adalah masalahnya dan waktunya untuk memburu untuk itu.Selain itu, saya tidak dapat mengingat di mana saya melihatnya (bahkan jika saya melihatnya di dokumen Android) tetapi saya ingat sesuatu tentang
PendingIntent
referensi Aktivitas dapat menyebabkan Aktivitas berperilaku seperti ini.Berikut ini tautan untuk
onStartCommand()
halaman dengan beberapa wawasan tentang proses depan non-pembunuhan.sumber
jadi satu-satunya hal yang benar-benar dapat saya pikirkan adalah jika Anda memiliki variabel statis yang merujuk langsung atau tidak langsung ke konteks. Bahkan sesuatu yang lebih banyak sebagai referensi ke bagian aplikasi. Saya yakin Anda sudah mencobanya tetapi saya akan menyarankannya untuk berjaga-jaga, coba batalkan SEMUA variabel statis Anda di onDestroy () hanya untuk memastikan pengumpul sampah mendapatkannya
sumber
Sumber kebocoran memori terbesar yang saya temukan disebabkan oleh beberapa referensi global, tingkat tinggi, atau yang sudah berlangsung lama. Jika Anda menyimpan "konteks" dalam variabel di mana saja, Anda mungkin mengalami kebocoran memori yang tidak dapat diprediksi.
sumber
Coba teruskan getApplicationContext () ke apa pun yang membutuhkan Konteks. Anda mungkin memiliki variabel global yang menyimpan referensi ke Aktivitas Anda dan mencegahnya agar tidak dikumpulkan.
sumber
Salah satu hal yang sangat membantu masalah memori dalam kasus saya adalah menyetel inPurgeable menjadi true untuk Bitmap saya. Lihat Mengapa saya TIDAK pernah menggunakan opsi inPurgeable BitmapFactory?dan pembahasan jawabannya untuk info lebih lanjut.
Jawaban Dianne Hackborn dan diskusi kita selanjutnya (juga terima kasih, CommonsWare) membantu memperjelas hal-hal tertentu yang membuat saya bingung, jadi terima kasih untuk itu.
sumber
Saya mengalami masalah yang sama dengan Anda. Saya sedang mengerjakan aplikasi perpesanan instan, untuk kontak yang sama, dimungkinkan untuk memulai ProfileActivity di ChatActivity, dan sebaliknya. Saya hanya menambahkan string ekstra ke dalam maksud untuk memulai aktivitas lain, ini mengambil informasi jenis kelas aktivitas pemula, dan id pengguna. Misalnya, ProfileActivity memulai ChatActivity, lalu di ChatActivity.onCreate, saya menandai jenis kelas invoker 'ProfileActivity' dan id pengguna, jika itu akan memulai Aktivitas, saya akan memeriksa apakah itu 'ProfileActivity' untuk pengguna atau tidak . Jika demikian, cukup panggil 'finish ()' dan kembali ke ProfileActivity sebelumnya alih-alih membuat yang baru. Kebocoran memori adalah hal lain.
sumber