Wir sehen eine Reihe von TimeoutExceptions
in GcWatcher.finalize, BinderProxy.finalize
und PlainSocketImpl.finalize
. 90 +% davon passieren auf Android 4.3. Wir erhalten Berichte darüber von Crittercism von Benutzern vor Ort.
Der Fehler ist eine Variation von: " 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)
Bisher hatten wir kein Glück, das Problem im Haus zu reproduzieren oder herauszufinden, was es verursacht haben könnte.
Irgendwelche Ideen, was das verursachen kann? Irgendeine Idee, wie man dies debuggt und herausfindet, welcher Teil der App dies verursacht? Alles, was Licht in das Problem bringt, hilft.
Weitere Stacktraces:
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
quelle
quelle
Antworten:
Vollständige Offenlegung - Ich bin der Autor des zuvor erwähnten Vortrags in TLV DroidCon.
Ich hatte die Gelegenheit, dieses Problem in vielen Android-Anwendungen zu untersuchen und mit anderen Entwicklern zu diskutieren, die darauf gestoßen sind - und wir kamen alle zum gleichen Punkt: Dieses Problem kann nicht vermieden, sondern nur minimiert werden.
Ich habe mir die Standardimplementierung des Android Garbage Collector-Codes genauer angesehen, um besser zu verstehen, warum diese Ausnahme ausgelöst wird und welche möglichen Ursachen dies haben könnte. Ich habe sogar eine mögliche Ursache beim Experimentieren gefunden.
Die Wurzel des Problems liegt an dem Punkt, an dem ein Gerät für eine Weile in den Ruhezustand wechselt. Dies bedeutet, dass das Betriebssystem beschlossen hat, den Batterieverbrauch zu senken, indem die meisten User Land-Prozesse für eine Weile gestoppt und der Bildschirm ausgeschaltet werden, wodurch die CPU-Zyklen verkürzt werden usw. Die Vorgehensweise erfolgt auf Linux-Systemebene, auf der die Prozesse während der Ausführung angehalten werden. Dies kann jederzeit während der normalen Ausführung der Anwendung geschehen, wird jedoch bei einem systemeigenen Systemaufruf beendet, da die Kontextumschaltung auf Kernelebene erfolgt. Also - hier schließt sich der Dalvik GC der Geschichte an.
Der Dalvik GC-Code (wie im Dalvik-Projekt auf der AOSP-Site implementiert) ist kein komplizierter Code. Die grundlegende Funktionsweise wird in meinen DroidCon-Folien behandelt. Was ich nicht behandelt habe, ist die grundlegende GC-Schleife - an dem Punkt, an dem der Sammler eine Liste von Objekten hat, die finalisiert (und zerstört) werden müssen. Die Schleifenlogik an der Basis kann folgendermaßen vereinfacht werden:
starting_timestamp
,finalize()
unddestroy()
bei Bedarf native aufrufen ,end_timestamp
,end_timestamp - starting_timestamp
) und vergleiche mit einem fest codierten Timeout-Wert von 10 Sekunden,java.util.concurrent.TimeoutException
und beenden Sie den Vorgang.Stellen Sie sich nun das folgende Szenario vor:
Die Anwendung läuft mit.
Dies ist keine Anwendung für Benutzer, sondern wird im Hintergrund ausgeführt.
Während dieser Hintergrundoperation werden Objekte erstellt, verwendet und müssen gesammelt werden, um Speicher freizugeben.
Die Anwendung stört sich nicht an einem WakeLock, da dies den Akku nachteilig beeinflusst und unnötig erscheint.
Dies bedeutet, dass die Anwendung von Zeit zu Zeit den GC aufruft.
Normalerweise wird der GC-Lauf ohne Probleme abgeschlossen.
Manchmal (sehr selten) entscheidet sich das System, mitten im GC-Lauf zu schlafen.
Dies geschieht, wenn Sie Ihre Anwendung lange genug ausführen und die Dalvik-Speicherprotokolle genau überwachen.
Wenn Sie nun die Zeitstempellogik der grundlegenden GC-Schleife berücksichtigen, kann das Gerät
start_stamp
beimdestroy()
nativen Aufruf eines Systemobjekts den Lauf starten, einen nehmen und in den Ruhezustand wechseln.Wenn es aufwacht und den Lauf fortsetzt,
destroy()
wird das beendet und das nächsteend_stamp
ist die Zeit, die derdestroy()
Anruf entgegengenommen wurde + die Schlafzeit.Wenn die Schlafzeit lang war (mehr als 10 Sekunden),
java.util.concurrent.TimeoutException
wird die geworfen.Ich habe dies in den Diagrammen gesehen, die mit dem Analyse-Python-Skript erstellt wurden - für Android-Systemanwendungen, nicht nur für meine eigenen überwachten Apps.
Sammeln Sie genügend Protokolle und Sie werden es schließlich sehen.
Endeffekt:
Das Problem kann nicht vermieden werden - Sie werden darauf stoßen, wenn Ihre App im Hintergrund ausgeführt wird.
Sie können Abhilfe schaffen, indem Sie ein WakeLock nehmen und verhindern, dass das Gerät in den Ruhezustand wechselt. Dies ist jedoch eine ganz andere Geschichte, neue Kopfschmerzen und möglicherweise ein weiteres Gespräch in einem anderen Umfeld.
Sie können das Problem minimieren, indem Sie GC-Anrufe reduzieren - wodurch das Szenario weniger wahrscheinlich wird (Tipps finden Sie auf den Folien).
Ich hatte noch keine Gelegenheit, den Dalvik 2 (auch bekannt als ART) GC-Code zu lesen, der über eine neue Generational Compacting-Funktion verfügt, oder Experimente mit einem Android Lollipop durchzuführen.
Hinzugefügt am 05.07.2015:
Nach Überprüfung der Aggregation von Absturzberichten für diesen Absturztyp scheinen diese Abstürze ab Version 5.0 des Android-Betriebssystems (Lollipop mit ART) nur 0,5% dieses Absturztyps zu verursachen. Dies bedeutet, dass die ART GC-Änderungen die Häufigkeit dieser Abstürze verringert haben.
Hinzugefügt am 01.06.2016:
Es sieht so aus, als hätte das Android-Projekt viele Informationen darüber hinzugefügt, wie der GC in Dalvik 2.0 (auch bekannt als ART) funktioniert.
Sie können hier darüber lesen - Debuggen der ART Garbage Collection .
Außerdem werden einige Tools erläutert, mit denen Sie Informationen zum GC-Verhalten Ihrer App abrufen können.
Das Senden eines SIGQUIT an Ihren App-Prozess führt im Wesentlichen zu einer ANR und speichert den Anwendungsstatus zur Analyse in einer Protokolldatei.
quelle
Wir sehen dies ständig in unserer App mithilfe von Crashlytics. Der Absturz ereignet sich normalerweise weit unten im Plattformcode. Eine kleine Auswahl:
Die Geräte, auf denen dies geschieht, sind überwiegend (aber nicht ausschließlich) Geräte von Samsung. Das könnte nur bedeuten, dass die meisten unserer Benutzer Samsung-Geräte verwenden. Alternativ könnte dies auf ein Problem mit Samsung-Geräten hinweisen. Ich bin mir nicht wirklich sicher.
Ich nehme an, dies beantwortet Ihre Fragen nicht wirklich, aber ich wollte nur bekräftigen, dass dies ziemlich häufig erscheint und nicht spezifisch für Ihre Anwendung ist.
quelle
Ich habe einige Folien zu diesem Thema gefunden.
http://de.slideshare.net/DroidConTLV/android-crash-analysis-and-the-dalvik-garbage-collector-tools-and-tips
In diesen Folien erklärt der Autor, dass es ein Problem mit GC zu sein scheint, wenn sich viele Objekte oder große Objekte auf dem Haufen befinden. Die Folie enthält auch einen Verweis auf eine Beispiel-App und ein Python-Skript, um dieses Problem zu analysieren.
https://github.com/oba2cat3/GCTest
https://github.com/oba2cat3/logcat2memorygraph
Außerdem habe ich in Kommentar 3 auf dieser Seite einen Hinweis gefunden: https://code.google.com/p/android/issues/detail?id=53418#c3
quelle
Wir haben das Problem gelöst, indem wir das gestoppt haben
FinalizerWatchdogDaemon
.Sie können die Methode im Lebenszyklus der Anwendung aufrufen, z
attachBaseContext()
. Aus dem gleichen Grund können Sie auch die Herstellung des Telefons angeben, um das Problem zu beheben. Es liegt an Ihnen.quelle
Zeitüberschreitung für Broadcast-Empfänger nach 10 Sekunden. Möglicherweise führen Sie einen asynchronen Anruf (falsch) von einem Rundfunkempfänger aus und 4.3 erkennt ihn tatsächlich.
quelle
Hier ist eine effektive Lösung von didi, um dieses Problem zu lösen. Da dieser Fehler sehr häufig und schwer zu finden ist, sieht es eher nach einem Systemproblem aus. Warum können wir ihn nicht direkt ignorieren? Natürlich können wir ihn hier ignorieren ist der Beispielcode:
Durch Festlegen eines speziellen Standard-Handlers für nicht erfasste Ausnahmen kann die Anwendung die Art und Weise ändern, in der nicht erfasste Ausnahmen für Threads behandelt werden, die bereits das vom System bereitgestellte Standardverhalten akzeptieren. Wenn ein Ungefangener
TimeoutException
aus einem Thread mit dem Namen geworfenFinalizerWatchdogDaemon
wird, blockiert dieser spezielle Handler die Handlerkette, der Systemhandler wird nicht aufgerufen, sodass ein Absturz vermieden wird.Durch Übung wurden keine anderen schlechten Effekte gefunden. Das GC-System funktioniert noch, Zeitüberschreitungen werden verringert, wenn die CPU-Auslastung abnimmt.
Weitere Informationen finden Sie unter: https://mp.weixin.qq.com/s/uFcFYO2GtWWiblotem2bGg
quelle
Eine Sache, die ausnahmslos zutrifft, ist, dass das Gerät zu diesem Zeitpunkt für einen gewissen Speicher erstickt (was normalerweise der Grund dafür ist, dass GC höchstwahrscheinlich ausgelöst wird).
Wie bereits von fast allen Autoren erwähnt, tritt dieses Problem auf, wenn Android versucht, GC auszuführen, während sich die App im Hintergrund befindet. In den meisten Fällen, in denen wir dies beobachtet haben, hat der Benutzer die App angehalten, indem er den Bildschirm gesperrt hat. Dies kann auch auf einen Speicherverlust irgendwo in der Anwendung hinweisen oder darauf, dass das Gerät bereits zu stark ausgelastet ist. Der einzig legitime Weg, dies zu minimieren, ist:
quelle
quelle
Die finalizeQueue ist möglicherweise zu lang
Ich denke, dass Java möglicherweise GC.SuppressFinalize () & GC.ReRegisterForFinalize () erfordert , damit der Benutzer die finalizedQueue- Länge explizit reduzieren kann
Wenn der Quellcode der JVM verfügbar ist, können Sie diese Methode selbst implementieren, z. B. Android ROM Maker
quelle
Es scheint wie ein Android Runtime-Fehler. Es scheint einen Finalizer zu geben, der in einem separaten Thread ausgeführt wird und die Methode finalize () für Objekte aufruft, wenn sie sich nicht im aktuellen Frame des Stacktraces befinden. Der folgende Code (der zur Überprüfung dieses Problems erstellt wurde) endete beispielsweise mit dem Absturz.
Lassen Sie uns einen Cursor haben, der etwas in der Finalize-Methode tut (z. B. SqlCipher-Cursor, do close (), der die derzeit verwendete Datenbank sperrt).
Und wir machen einige lang laufende Sachen, nachdem wir den Cursor geöffnet haben:
Dies verursacht folgenden Fehler:
Die Produktionsvariante mit SqlCipher ist sehr ähnlich:
Fortsetzen: Schließen Sie die Cursor so schnell wie möglich. Zumindest auf Samsung S8 mit Android 7, wo das Problem gesehen wurde.
quelle
Für Klassen, die Sie erstellen (dh die nicht Teil von Android sind), ist es möglich, den Absturz vollständig zu vermeiden.
Jede Klasse, die implementiert,
finalize()
hat eine unvermeidbare Wahrscheinlichkeit eines Absturzes, wie von @oba erklärt. Verwenden Sie also a, anstatt Finalizer für die Bereinigung zu verwendenPhantomReferenceQueue
.Ein Beispiel finden Sie in der Implementierung in React Native: https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/jni/DestructorThread.java
quelle