Die Android-Laufzeit (ART) ist die Standardlaufzeit für Geräte mit Android 5.0 (API-Level 21) und höher. Diese Laufzeit bietet eine Reihe von Funktionen, die die Leistung und Flüssigkeit der Android-Plattform und -Apps verbessern. Weitere Informationen zu den neuen Funktionen von ART finden Sie unter Einführung in ART.
Einige Techniken, die bei Dalvik funktionieren, funktionieren jedoch nicht bei ART. In diesem Dokument erfahren Sie, worauf Sie beim Migrieren einer vorhandenen Anwendung achten sollten, damit sie mit ART kompatibel ist. Die meisten Anwendungen sollten einfach mit ART funktionieren.
Probleme bei der automatischen Speicherbereinigung beheben
Unter Dalvik ist es in Anwendungen häufig hilfreich, System.gc()
explizit aufzurufen, um die automatische Speicherbereinigung (GC) anzufordern. Dies sollte bei ART weitaus weniger notwendig sein, insbesondere wenn Sie die automatische Speicherbereinigung aufrufen, um GC_FOR_ALLOC
-Vorgänge zu verhindern oder die Fragmentierung zu reduzieren. Wenn Sie prüfen möchten, welche Laufzeit verwendet wird, rufen Sie System.getProperty("java.vm.version")
auf. Wenn ART verwendet wird, ist der Wert der Eigenschaft "2.0.0"
oder höher.
ART verwendet den Collector Concurrent Copying (CC), der gleichzeitig den Java-Heap verdichtet. Aus diesem Grund sollten Sie Verfahren vermeiden, die mit der Komprimierung der automatischen Speicherbereinigung nicht kompatibel sind (z. B. das Speichern von Verweisen auf Objektinstanzdaten). Dies ist besonders wichtig für Anwendungen, die das Java Native Interface (JNI) verwenden. Weitere Informationen finden Sie unter JNI-Probleme verhindern.
JNI-Probleme vermeiden
Die JNI von ART ist etwas strenger als die von Dalvik. Es empfiehlt sich besonders, den CheckJNI-Modus zu verwenden, um häufige Probleme zu erkennen. Wenn Ihre App C-/C++-Code verwendet, lesen Sie den folgenden Artikel:
Fehlerbehebung in Android JNI mit CheckJNI
JNI-Code wird auf Probleme bei der automatischen Speicherbereinigung geprüft
Der Collector „Gleichzeitiges Kopieren“ (CC) kann Objekte zur Verdichtung im Arbeitsspeicher verschieben. Führen Sie mit C-/C++-Code keine Vorgänge aus, die mit der Komprimierung von GC nicht kompatibel sind. Wir haben CheckJNI verbessert, um einige potenzielle Probleme zu erkennen (wie unter Änderungen bei JNI Local Reference in ICS beschrieben).
Ein besonders wichtiger Bereich ist die Verwendung der Funktionen Get...ArrayElements()
und Release...ArrayElements()
. Bei Laufzeiten mit nicht komprimierter Speicherbereinigung geben die Get...ArrayElements()
-Funktionen in der Regel einen Verweis auf den tatsächlichen Arbeitsspeicher zurück, der das Array-Objekt unterstützt. Wenn Sie eine Änderung an einem der zurückgegebenen Arrayelemente vornehmen, wird das Array-Objekt selbst geändert. Die Argumente für Release...ArrayElements()
werden normalerweise ignoriert. Wenn jedoch die Komprimierung von Speicherbereinigungsvorgängen verwendet wird, können die Get...ArrayElements()
-Funktionen eine Kopie des Arbeitsspeichers zurückgeben. Wenn Sie die Referenz bei der Komprimierung von GC missbrauchen, kann dies zu Speicherschäden oder anderen Problemen führen. Beispiele:
- Wenn Sie Änderungen an den zurückgegebenen Arrayelementen vornehmen, müssen Sie anschließend die entsprechende
Release...ArrayElements()
-Funktion aufrufen, damit die vorgenommenen Änderungen korrekt zurück in das zugrunde liegende Arrayobjekt kopiert werden. - Wenn Sie die Elemente des Speicherarrays freigeben, müssen Sie abhängig von den vorgenommenen Änderungen den entsprechenden Modus verwenden:
- Wenn Sie keine Änderungen an den Arrayelementen vorgenommen haben, verwenden Sie den Modus
JNI_ABORT
. Dadurch wird der Arbeitsspeicher freigegeben, ohne Änderungen in das zugrunde liegende Arrayobjekt zu kopieren. - Wenn Sie Änderungen am Array vorgenommen haben und den Verweis nicht mehr benötigen, verwenden Sie den Code
0
. Dadurch wird das Array-Objekt aktualisiert und die Kopie des Arbeitsspeichers freigegeben. - Wenn Sie Änderungen am Array vorgenommen haben, für das Sie einen Commit durchführen möchten, und Sie die Kopie des Arrays behalten möchten, verwenden Sie
JNI_COMMIT
. Dadurch wird das zugrunde liegende Arrayobjekt aktualisiert und die Kopie beibehalten.
- Wenn Sie keine Änderungen an den Arrayelementen vorgenommen haben, verwenden Sie den Modus
- Wenn Sie
Release...ArrayElements()
aufrufen, wird der gleiche Pointer zurückgegeben, der ursprünglich vonGet...ArrayElements()
zurückgegeben wurde. Es ist beispielsweise nicht sicher, den ursprünglichen Zeiger zu erhöhen (um die zurückgegebenen Arrayelemente zu durchsuchen) und dann den inkrementierten Zeiger anRelease...ArrayElements()
zu übergeben. Das Übergeben dieses geänderten Zeigers kann dazu führen, dass der falsche Arbeitsspeicher freigegeben wird, was zu Speicherschäden führen kann.
Fehlerbehandlung
Die JNI von ART gibt Fehler in einer Reihe von Fällen aus, in denen Dalvik dies nicht tut. (Noch einmal) können Sie viele solcher Fälle durch Tests mit CheckJNI erkennen.
Wenn beispielsweise RegisterNatives
mit einer Methode aufgerufen wird, die nicht existiert (möglicherweise weil die Methode von einem Tool wie ProGuard entfernt wurde), löst ART jetzt NoSuchMethodError
ordnungsgemäß aus:
08-12 17:09:41.082 13823 13823 E AndroidRuntime: FATAL EXCEPTION: main 08-12 17:09:41.082 13823 13823 E AndroidRuntime: java.lang.NoSuchMethodError: no static or non-static method "Lcom/foo/Bar;.native_frob(Ljava/lang/String;)I" 08-12 17:09:41.082 13823 13823 E AndroidRuntime: at java.lang.Runtime.nativeLoad(Native Method) 08-12 17:09:41.082 13823 13823 E AndroidRuntime: at java.lang.Runtime.doLoad(Runtime.java:421) 08-12 17:09:41.082 13823 13823 E AndroidRuntime: at java.lang.Runtime.loadLibrary(Runtime.java:362) 08-12 17:09:41.082 13823 13823 E AndroidRuntime: at java.lang.System.loadLibrary(System.java:526)
ART protokolliert auch einen Fehler (sichtbar in Logcat), wenn RegisterNatives
ohne Methoden aufgerufen wird:
W/art ( 1234): JNI RegisterNativeMethods: attempt to register 0 native methods for <classname>
Außerdem geben die JNI-Funktionen GetFieldID()
und GetStaticFieldID()
jetzt korrekt NoSuchFieldError
aus, anstatt einfach null zurückzugeben. In ähnlicher Weise geben GetMethodID()
und GetStaticMethodID()
jetzt korrekt NoSuchMethodError
aus.
Dies kann aufgrund der unbehandelten Ausnahmen oder der Ausnahmen, die an Java-Aufrufer von nativem Code ausgegeben werden, zu CheckJNI-Fehlern führen. Daher ist es besonders wichtig, ART-kompatible Anwendungen mit dem CheckJNI-Modus zu testen.
ART erwartet, dass Nutzer der JNI-CallNonvirtual...Method()
-Methoden (z. B. CallNonvirtualVoidMethod()
) gemäß der JNI-Spezifikation die Deklarationsklasse der Methode und keine Unterklasse verwenden.
Probleme mit der Stackgröße vermeiden
Dalvik hatte separate Stacks für nativen und Java-Code mit einer standardmäßigen Java-Stackgröße von 32 KB und einer Standardgröße für native Stacks von 1 MB. ART hat einen einheitlichen
Stack für bessere Lokalität. Normalerweise sollte der ART-Stack Thread
etwa die gleiche Größe wie Dalvik haben. Wenn Sie jedoch explizit Stackgrößen festlegen, müssen Sie diese Werte für Anwendungen, die in ART ausgeführt werden, möglicherweise noch einmal überprüfen.
- Sehen Sie sich in Java die Aufrufe des Konstruktors
Thread
an, die eine explizite Stapelgröße angeben. Sie müssen sie beispielsweise erhöhen, wennStackOverflowError
auftritt. - Sehen Sie sich in C/C++ die Verwendung von
pthread_attr_setstack()
undpthread_attr_setstacksize()
für Threads an, die auch Java-Code über JNI ausführen. Hier ein Beispiel für den Fehler, der protokolliert wird, wenn eine Anwendung versucht, die JNIAttachCurrentThread()
aufzurufen, wenn die pthread-Größe zu klein ist:F/art: art/runtime/thread.cc:435] Attempt to attach a thread with a too-small stack (16384 bytes)
Objektmodelländerungen
Dalvik hat Unterklassen fälschlicherweise erlaubt, Paket-private Methoden zu überschreiben. ART gibt in folgenden Fällen eine Warnung aus:
Before Android 4.1, method void com.foo.Bar.quux() would have incorrectly overridden the package-private method in com.quux.Quux
Wenn Sie die Methode einer Klasse in einem anderen Paket überschreiben möchten, deklarieren Sie die Methode als public
oder protected
.
Object
hat jetzt private Felder. Achten Sie bei Anwendungen, die Felder in ihren Klassenhierarchien berücksichtigen, darauf, nicht die Felder von Object
zu untersuchen. Wenn Sie z. B. eine Klassenhierarchie
im Rahmen eines Serialisierungs-Frameworks iterieren,
Class.getSuperclass() == java.lang.Object.class
anstatt fortzufahren, bis die Methode null
zurückgibt.
Der Proxy InvocationHandler.invoke()
empfängt jetzt null
, wenn keine Argumente anstelle eines leeren Arrays vorhanden sind. Dieses Verhalten war zuvor dokumentiert, wurde in Dalvik aber nicht korrekt gehandhabt. Bei früheren Versionen von Mockito treten hierbei Probleme auf. Verwenden Sie daher beim Testen mit ART eine aktualisierte Mockito-Version.
Probleme bei der AOT-Kompilierung beheben
Die AOT-Java-Kompilierung (Ahead-Of-Time) von ART sollte für jeglichen Standard-Java-Code funktionieren. Die Kompilierung wird mit dem dex2oat
-Tool von ART durchgeführt. Wenn bei der Installation Probleme mit dex2oat
auftreten, lass es uns wissen (siehe Probleme melden), damit wir sie so schnell wie möglich beheben können. Beachten Sie folgende Punkte:
- ART führt bei der Installation eine strengere Bytecode-Überprüfung durch als Dalvik. Mit den Android-Build-Tools erstellter Code sollte in Ordnung sein. Einige Nachbearbeitungstools (insbesondere Tools, die Verschleierung ausführen) können jedoch ungültige Dateien erzeugen, die von Dalvik toleriert, aber von ART abgelehnt werden. Wir arbeiten mit Toolanbietern zusammen, um solche Probleme zu finden und zu beheben. In vielen Fällen können diese Probleme durch das Herunterladen der neuesten Versionen der Tools und das Neugenerieren der DEX-Dateien behoben werden.
- Einige typische Probleme, die von der ART-Überprüfung gemeldet werden, sind:
- ungültiger Kontrollfluss
monitorenter
/monitorexit
unausgeglichen- Listengröße des Parametertyps mit 0 Länge
- Einige Anwendungen haben Abhängigkeiten vom installierten Dateiformat
.odex
in/system/framework
,/data/dalvik-cache
oder im optimierten Ausgabeverzeichnis vonDexClassLoader
. Diese Dateien sind jetzt ELF-Dateien und keine erweiterte Form von DEX-Dateien. ART versucht zwar, denselben Namens- und Sperrregeln wie Dalvik zu folgen, Apps sollten jedoch nicht vom Dateiformat abhängig sein; das Format kann ohne Vorankündigung geändert werden.Hinweis: In Android 8.0 (API-Level 26) und höher wurde das optimierte Ausgabeverzeichnis
DexClassLoader
eingestellt. Weitere Informationen finden Sie in der Dokumentation zum KonstruktorDexClassLoader()
.
Probleme mit der Berichterstellung
Wenn Probleme auftreten, die nicht auf JNI-Probleme in der App zurückzuführen sind, melden Sie sie über den Issue Tracker für das Android Open Source Project unter https://code.google.com/p/android/issues/list.
Füge ein "adb bugreport"
und einen Link zur App im Google Play Store hinzu, sofern verfügbar. Hängen Sie andernfalls, falls möglich, ein APK an, das das Problem reproduziert. Beachten Sie, dass Probleme (einschließlich Anhängen) öffentlich sichtbar sind.