Kami melihat beberapa TimeoutExceptions
di GcWatcher.finalize, BinderProxy.finalize
, dan PlainSocketImpl.finalize
. 90+% dari mereka terjadi di Android 4.3. Kami menerima laporan ini dari Crittercism dari pengguna di lapangan.
Kesalahan adalah variasi dari: " com.android.internal.BinderInternal$GcWatcher.finalize() timed out after 10 seconds
"
java.util.concurrent.TimeoutException: android.os.BinderProxy.finalize() timed out after 10 seconds
at android.os.BinderProxy.destroy(Native Method)
at android.os.BinderProxy.finalize(Binder.java:459)
at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:187)
at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:170)
at java.lang.Thread.run(Thread.java:841)
Sejauh ini kami belum beruntung mereproduksi masalah di rumah atau mencari tahu apa yang menyebabkannya.
Adakah ide yang dapat menyebabkan ini? Adakah yang tahu cara men-debug ini dan mencari tahu bagian mana dari aplikasi yang menyebabkan ini? Apa pun yang menjelaskan masalah ini akan membantu.
Stacktraces lainnya:
1 android.os.BinderProxy.destroy
2 android.os.BinderProxy.finalize Binder.java, line 482
3 java.lang.Daemons$FinalizerDaemon.doFinalize Daemons.java, line 187
4 java.lang.Daemons$FinalizerDaemon.run Daemons.java, line 170
5 java.lang.Thread.run Thread.java, line 841
2
1 java.lang.Object.wait
2 java.lang.Object.wait Object.java, line 401
3 java.lang.ref.ReferenceQueue.remove ReferenceQueue.java, line 102
4 java.lang.ref.ReferenceQueue.remove ReferenceQueue.java, line 73
5 java.lang.Daemons$FinalizerDaemon.run Daemons.java, line 170
6 java.lang.Thread.run
3
1 java.util.HashMap.newKeyIterator HashMap.java, line 907
2 java.util.HashMap$KeySet.iterator HashMap.java, line 913
3 java.util.HashSet.iterator HashSet.java, line 161
4 java.util.concurrent.ThreadPoolExecutor.interruptIdleWorkers ThreadPoolExecutor.java, line 755
5 java.util.concurrent.ThreadPoolExecutor.interruptIdleWorkers ThreadPoolExecutor.java, line 778
6 java.util.concurrent.ThreadPoolExecutor.shutdown ThreadPoolExecutor.java, line 1357
7 java.util.concurrent.ThreadPoolExecutor.finalize ThreadPoolExecutor.java, line 1443
8 java.lang.Daemons$FinalizerDaemon.doFinalize Daemons.java, line 187
9 java.lang.Daemons$FinalizerDaemon.run Daemons.java, line 170
10 java.lang.Thread.run
4
1 com.android.internal.os.BinderInternal$GcWatcher.finalize BinderInternal.java, line 47
2 java.lang.Daemons$FinalizerDaemon.doFinalize Daemons.java, line 187
3 java.lang.Daemons$FinalizerDaemon.run Daemons.java, line 170
4 java.lang.Thread.run
android
garbage-collection
emmby
sumber
sumber
Jawaban:
Pengungkapan penuh - Saya penulis pembicaraan yang disebutkan sebelumnya dalam TLV DroidCon.
Saya memiliki kesempatan untuk memeriksa masalah ini di banyak aplikasi Android, dan mendiskusikannya dengan pengembang lain yang menjumpainya - dan kami semua mencapai titik yang sama: masalah ini tidak dapat dihindari, hanya diminimalkan.
Saya melihat lebih dekat pada implementasi default kode kolektor Android Garbage, untuk lebih memahami mengapa pengecualian ini dilemparkan dan tentang apa yang mungkin menjadi penyebabnya. Saya bahkan menemukan akar penyebab yang mungkin selama eksperimen.
Akar masalahnya adalah pada titik perangkat "Goes to Sleep" untuk sementara waktu - ini berarti bahwa OS telah memutuskan untuk menurunkan konsumsi baterai dengan menghentikan sebagian besar proses Pengguna Tanah untuk sementara waktu, dan mematikan Layar, mematikan siklus CPU , dll. Cara ini dilakukan - ada pada level sistem Linux di mana proses-proses dijeda pada pertengahan berjalan. Ini dapat terjadi kapan saja selama eksekusi Aplikasi normal, tetapi itu akan berhenti pada panggilan sistem Native, karena pengalihan konteks dilakukan pada level kernel. Jadi - di sinilah Dalvik GC bergabung dengan cerita ini.
Kode Dalvik GC (seperti yang diterapkan dalam proyek Dalvik di situs AOSP) bukan sepotong kode yang rumit. Cara dasar kerjanya dicakup dalam slide DroidCon saya. Yang tidak saya bahas adalah loop GC dasar - pada titik di mana kolektor memiliki daftar Objek untuk diselesaikan (dan dihancurkan). Logika loop di pangkalan dapat disederhanakan seperti ini:
starting_timestamp
,finalize()
dan panggilan aslidestroy()
jika diperlukan,end_timestamp
,end_timestamp - starting_timestamp
) dan membandingkannya dengan nilai batas waktu kode 10 detik,java.util.concurrent.TimeoutException
dan matikan prosesnya.Sekarang pertimbangkan skenario berikut:
Aplikasi berjalan bersama melakukan hal tersebut.
Ini bukan aplikasi yang menghadap pengguna, ini berjalan di latar belakang.
Selama operasi latar belakang ini, objek dibuat, digunakan dan perlu dikumpulkan untuk melepaskan memori.
Aplikasi tidak mengganggu dengan WakeLock - karena ini akan mempengaruhi baterai dengan buruk, dan sepertinya tidak perlu.
Ini berarti Aplikasi akan meminta GC dari waktu ke waktu.
Biasanya proses GC selesai tanpa hambatan.
Terkadang (sangat jarang) sistem akan memutuskan untuk tidur di tengah menjalankan GC.
Ini akan terjadi jika Anda menjalankan aplikasi Anda cukup lama, dan memonitor log memori Dalvik dengan cermat.
Sekarang - pertimbangkan logika cap waktu dari loop GC dasar - adalah mungkin bagi perangkat untuk memulai menjalankan, mengambil
start_stamp
, dan pergi tidur padadestroy()
panggilan asli pada objek sistem.Ketika bangun dan melanjutkan lari,
destroy()
akan selesai, dan berikutnyaend_stamp
akan menjadi waktu yang dibutuhkandestroy()
panggilan + waktu tidur.Jika waktu tidur panjang (lebih dari 10 detik),
java.util.concurrent.TimeoutException
akan terlempar.Saya telah melihat ini dalam grafik yang dihasilkan dari skrip python analisis - untuk Aplikasi Sistem Android, bukan hanya aplikasi saya sendiri yang dipantau.
Kumpulkan log yang cukup dan pada akhirnya Anda akan melihatnya.
Intinya:
Masalahnya tidak dapat dihindari - Anda akan menjumpainya jika aplikasi Anda berjalan di latar belakang.
Anda dapat mengurangi dengan mengambil WakeLock, dan mencegah perangkat dari tidur, tapi itu adalah cerita yang berbeda sama sekali, dan sakit kepala baru, dan mungkin pembicaraan lain di penipu lain.
Anda dapat meminimalkan masalah dengan mengurangi panggilan GC - membuat skenario lebih kecil kemungkinannya (tips ada di slide).
Saya belum memiliki kesempatan untuk membahas kode Dalvik 2 (alias ART) GC - yang menawarkan fitur Generational Compacting baru, atau melakukan percobaan apa pun pada Android Lollipop.
Ditambahkan 7/5/2015:
Setelah meninjau agregasi laporan kerusakan untuk jenis kerusakan ini, sepertinya kerusakan ini dari versi 5.0+ dari OS Android (Lollipop dengan ART) hanya menyumbang 0,5% dari jenis kerusakan ini. Ini berarti bahwa perubahan ART GC telah mengurangi frekuensi gangguan ini.
Ditambahkan 6/1/2016:
Sepertinya proyek Android telah menambahkan banyak info tentang cara kerja GC di Dalvik 2.0 (alias ART).
Anda dapat membacanya di sini - Debugging ART Garbage Collection .
Itu juga membahas beberapa alat untuk mendapatkan informasi tentang perilaku GC untuk aplikasi Anda.
Mengirim SIGQUIT ke proses aplikasi Anda pada dasarnya akan menyebabkan ANR, dan membuang status aplikasi ke file log untuk dianalisis.
sumber
Kami melihat ini terus-menerus, di seluruh aplikasi kami, menggunakan Crashlytics. Kecelakaan biasanya terjadi pada kode platform. Sampel kecil:
Perangkat tempat hal ini terjadi sebagian besar (tetapi tidak secara eksklusif) perangkat yang diproduksi oleh Samsung. Itu bisa berarti bahwa sebagian besar pengguna kami menggunakan perangkat Samsung; bergantian itu bisa menunjukkan masalah dengan perangkat Samsung. Saya tidak begitu yakin.
Saya kira ini tidak benar-benar menjawab pertanyaan Anda, tetapi saya hanya ingin menegaskan bahwa ini tampaknya cukup umum, dan tidak spesifik untuk aplikasi Anda.
sumber
Saya menemukan beberapa slide tentang masalah ini.
http://de.slideshare.net/DroidConTLV/android-crash-analysis-and-the-dalvik-garbage-collector-tools-and-tips
Dalam slide ini, penulis mengatakan bahwa sepertinya ada masalah dengan GC, jika ada banyak objek atau objek besar di tumpukan. Slide juga menyertakan referensi ke aplikasi sampel dan skrip python untuk menganalisis masalah ini.
https://github.com/oba2cat3/GCTest
https://github.com/oba2cat3/logcat2memorygraph
Selanjutnya saya menemukan petunjuk di komentar # 3 di sisi ini: https://code.google.com/p/android/issues/detail?id=53418#c3
sumber
Kami memecahkan masalah dengan menghentikan
FinalizerWatchdogDaemon
.Anda dapat memanggil metode dalam siklus hidup Aplikasi, seperti
attachBaseContext()
. Untuk alasan yang sama, Anda juga dapat menentukan pembuatan ponsel untuk memperbaiki masalahnya, terserah Anda.sumber
Batas waktu Siaran Penerima setelah 10 detik. Mungkin Anda melakukan panggilan asinkron (salah) dari penerima siaran dan 4.3 benar-benar mendeteksinya.
sumber
Ini adalah solusi yang efektif dari didi untuk menyelesaikan masalah ini, Karena bug ini sangat umum dan sulit untuk menemukan penyebabnya, Kelihatannya lebih seperti masalah sistem, Mengapa kita tidak bisa mengabaikannya secara langsung? Tentu saja kita dapat mengabaikannya, Di sini adalah kode contoh:
Dengan menetapkan handler pengecualian uncaught default khusus, aplikasi dapat mengubah cara penanganan uncaught exception untuk utas yang sudah menerima perilaku default apa pun yang disediakan sistem. Ketika tidak tertangkap
TimeoutException
dilemparkan dari utas bernamaFinalizerWatchdogDaemon
, handler khusus ini akan memblokir rantai handler, handler sistem tidak akan dipanggil, jadi crash akan dihindari.Melalui latihan, tidak ada efek buruk lain yang ditemukan. Sistem GC masih berfungsi, batas waktu dikurangi karena penggunaan CPU berkurang.
Untuk detail lebih lanjut lihat: https://mp.weixin.qq.com/s/uFcFYO2GtWWiblotem2bGg
sumber
Satu hal yang selalu benar adalah bahwa pada saat ini, perangkat akan mati lemas untuk beberapa memori (yang biasanya menjadi alasan GC kemungkinan besar dipicu).
Seperti yang disebutkan oleh hampir semua penulis sebelumnya, masalah ini muncul ketika Android mencoba menjalankan GC saat aplikasi di latar belakang. Dalam sebagian besar kasus di mana kami mengamatinya, pengguna menjeda aplikasi dengan mengunci layar mereka. Ini mungkin juga menunjukkan kebocoran memori di suatu tempat dalam aplikasi, atau perangkat sudah terlalu dimuat. Jadi satu-satunya cara yang sah untuk menguranginya adalah:
sumber
sumber
FinalizeQueue mungkin terlalu panjang
saya pikir java mungkin memerlukan GC.SuppressFinalize () & GC.ReRegisterForFinalize () untuk memungkinkan pengguna mengurangi panjang finalizedQueue secara eksplisit
jika kode sumber JVM tersedia, dapat menerapkan metode ini sendiri, seperti pembuat ROM Android
sumber
Sepertinya bug Android Runtime. Tampaknya ada finalizer yang berjalan di utasnya yang terpisah dan memanggil metode finalize () pada objek jika mereka tidak berada dalam bingkai stacktrace saat ini. Misalnya kode berikut (dibuat untuk memverifikasi masalah ini) berakhir dengan crash.
Mari kita memiliki beberapa kursor yang melakukan sesuatu dalam metode finalisasi (mis. SqlCipher, do close () yang mengunci ke database yang sedang digunakan)
Dan kami melakukan beberapa hal yang berjalan lama setelah membuka kursor:
Ini menyebabkan kesalahan berikut:
Varian produksi dengan SqlCipher sangat mirip:
Lanjutkan: Tutup kursor ASAP. Setidaknya pada Samsung S8 dengan Android 7 di mana masalah telah terlihat.
sumber
Untuk kelas yang Anda buat (mis. Bukan bagian dari Android) dimungkinkan untuk menghindari crash sepenuhnya.
Setiap kelas yang mengimplementasikan
finalize()
memiliki beberapa kemungkinan crash yang tidak dapat dihindari seperti yang dijelaskan oleh @oba. Jadi, alih-alih menggunakan finalizer untuk melakukan pembersihan, gunakan aPhantomReferenceQueue
.Sebagai contoh, periksa penerapannya di React Native: https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/jni/DestructorThread.java
sumber