ART-Speicherbereinigung debuggen

Auf dieser Seite wird beschrieben, wie Sie Fehler bei der Korrektheit und Leistung der automatischen Speicherbereinigung (Garbage Collection, GC) der Android Runtime (ART) beheben. Dort erfahren Sie, wie Sie die Optionen zur Bestätigung von Google-Kontos verwenden, Lösungen für Fehler bei der Bestätigung von Google-Konten finden und Leistungsprobleme mit Google-Konten messen und beheben.

Informationen zur Arbeit mit ART finden Sie auf den Seiten in diesem Abschnitt ART und Dalvik und im Artikel Dalvik-Ausführbares Format. Weitere Informationen zum Überprüfen des App-Verhaltens finden Sie unter App-Verhalten in Android Runtime (ART) überprüfen.

ART GC-Übersicht

ART hat mehrere GC-Pläne, die aus verschiedenen Garbage Collectorn bestehen. Ab Android 8 (Oreo) ist die Standardeinstellung „Gleichzeitiges Kopieren“ (Concurrent Copying, CC). Der andere GC-Plan ist der Concurrent Mark Sweep (CMS).

Zu den wichtigsten Merkmalen der gleichzeitigen Kopier-GC gehören:

  • CC ermöglicht die Verwendung eines Bump-Pointer-Allocators namens RegionTLAB. Dadurch wird jedem App-Thread ein threadlokaler Allokationsbuffer (TLAB) zugewiesen, der dann Objekte aus seinem TLAB ohne Synchronisierung durch Anheben des „Top“-Zeigers zuweisen kann.
  • CC führt eine Heap-Defragmentierung durch, indem Objekte gleichzeitig kopiert werden, ohne App-Threads anzuhalten. Dies wird mithilfe einer Lesesperre erreicht, die Lesezugriffe auf den Heap abfängt, ohne dass der App-Entwickler eingreifen muss.
  • Der GC hat nur eine kurze Pause, die zeitlich konstant ist und von der Heap-Größe abhängt.
  • CC wird in Android 10 und höher zu einer generationellen GC erweitert. So lassen sich junge Objekte, die oft ziemlich schnell nicht mehr erreichbar sind, mit wenig Aufwand erfassen. Dadurch wird der GC-Durchsatz erhöht und die Notwendigkeit, einen Full-Heap-GC auszuführen, erheblich verzögert.

Die andere GC, die von ART noch unterstützt wird, ist CMS. Diese GC unterstützt auch die Komprimierung, jedoch nicht gleichzeitig. Die Verdichtung wird vermieden, bis die App im Hintergrund ausgeführt wird. In diesem Fall werden die App-Threads angehalten, um die Verdichtung durchzuführen. Eine Datenkomprimierung ist auch dann erforderlich, wenn eine Objektzuweisung aufgrund von Fragmentierung fehlschlägt. In diesem Fall reagiert die App möglicherweise einige Zeit lang nicht.

Da das CMS selten komprimiert und daher kostenlose Objekte möglicherweise nicht zusammenhängend sind, wird ein freilistenbasierter Allocator namens RosAlloc verwendet. Die Zuordnungskosten sind im Vergleich zu RegionTLAB höher. Schließlich kann die Speichernutzung für den Java-Haufen bei CMS aufgrund der internen Fragmentierung höher sein als bei CC.

Überprüfung der Google-Kontenverwaltung und Leistungsoptionen

GC-Typ ändern

OEMs können den GC-Typ ändern. Dazu müssen Sie die Umgebungsvariable ART_USE_READ_BARRIER zum Zeitpunkt der Erstellung festlegen. Der Standardwert ist „wahr“, wodurch der CC-Erfassungsmechanismus aktiviert wird, da er die Lesesperre verwendet. Für CMS sollte diese Variable explizit auf „falsch“ gesetzt werden.

Unter Android 10 und höher wird der CC-Messer standardmäßig im Generationsmodus ausgeführt. Um den Generationsmodus zu deaktivieren, kann das Befehlszeilenargument -Xgc:nogenerational_cc verwendet werden. Alternativ kann die Systemeigenschaft so festgelegt werden:

adb shell setprop dalvik.vm.gctype nogenerational_cc
Der CMS-Erfassungstool wird immer im Generationsmodus ausgeführt.

Heap prüfen

Die Heap-Bestätigung ist wahrscheinlich die nützlichste GC-Option zum Beheben von GC-bezogenen Fehlern oder Heap-Beschädigungen. Wenn Sie die Heap-Überprüfung aktivieren, prüft der Garbage Collector an einigen Stellen während des Garbage-Collection-Prozesses die Korrektheit des Heaps. Für die Überprüfung des Heaps stehen dieselben Optionen zur Verfügung wie für die Änderung des GC-Typs. Wenn die Heap-Überprüfung aktiviert ist, werden die Wurzeln geprüft und dafür gesorgt, dass erreichbare Objekte nur auf andere erreichbare Objekte verweisen. Die GC-Bestätigung wird aktiviert, indem die folgenden -Xgc-Werte übergeben werden:

  • Wenn diese Option aktiviert ist, führt [no]preverify eine Heap-Überprüfung durch, bevor der GC gestartet wird.
  • Wenn diese Option aktiviert ist, führt [no]presweepingverify eine Heap-Bestätigung durch, bevor der Garbage Collector-Bereinigungsprozess gestartet wird.
  • Wenn diese Option aktiviert ist, führt [no]postverify eine Heap-Überprüfung durch, nachdem der GC-Sweeping-Vorgang abgeschlossen ist.
  • [no]preverify_rosalloc, [no]postsweepingverify_rosalloc und [no]postverify_rosalloc sind zusätzliche GC-Optionen, mit denen nur der Status der internen Ressourcenerfassung von RosAlloc überprüft wird. Daher sind sie nur für den CMS-Erfassungstool verfügbar, der den RosAlloc-Allocator verwendet. Vor allem wird geprüft, ob die magischen Werte mit den erwarteten Konstanten übereinstimmen und alle kostenlosen Speicherblöcke in der free_page_runs_-Zuordnung registriert sind.

Leistung

Es gibt zwei Haupttools zur Messung der GC-Leistung: GC-Timing-Dumps und Systrace. Es gibt auch eine erweiterte Version von Systrace namens Perfetto. Mit Systrace und Perfetto können Sie visuell Leistungsprobleme der Garbage Collection messen, um zu ermitteln, welche GCs zu langen Pausen führen oder App-Threads vorzeitig beenden. Obwohl sich der ART-GC im Laufe der Zeit erheblich verbessert hat, kann ein schlechtes Mutator-Verhalten wie eine übermäßige Allokation weiterhin zu Leistungsproblemen führen.

Sammlungsstrategie

Der CC-GC führt entweder eine Young-GC oder eine Full-Heap-GC aus. Idealerweise wird der junge GC häufiger ausgeführt. Der GC führt Young-CC-Sammlungen durch, bis der Durchsatz (berechnet anhand der freigegebenen Byte pro Sekunde der GC-Dauer) des gerade abgeschlossenen Sammlungszyklus unter dem mittleren Durchsatz der CC-Sammlungen des gesamten Heaps liegt. In diesem Fall wird für die nächste gleichzeitige GC der CC für den gesamten Heap anstelle des CC für den jungen Heap ausgewählt. Nach Abschluss der vollständigen Heap-Sammlung wird bei der nächsten GC wieder zur jungen CC gewechselt. Ein wichtiger Faktor für den Erfolg dieser Strategie ist, dass der junge CC das Limit für den Heap-Footprint nach Abschluss nicht anpasst. Dies führt dazu, dass immer mehr junge CCs auftreten, bis der Durchsatz unter den CCs des vollständigen Heaps liegt, was wiederum zu einer Vergrößerung des Heaps führt.

SIGQUIT verwenden, um Informationen zur GC-Leistung zu erhalten

Wenn Sie die GC-Leistungszeiten für Apps abrufen möchten, senden Sie SIGQUIT an bereits laufende Apps oder übergeben Sie -XX:DumpGCPerformanceOnShutdown an dalvikvm, wenn Sie ein Befehlszeilenprogramm starten. Wenn eine App das ANR-Anfragesignal (SIGQUIT) erhält, werden Informationen zu ihren Sperren, Threadstacks und zur GC-Leistung ausgegeben.

So rufen Sie GC-Timing-Dumps ab:

adb shell kill -s QUIT PID

Dadurch wird eine Datei mit dem Datum und der Uhrzeit im Namen (z. B. anr_2020-07-13-19-23-39-817) in /data/anr/ erstellt. Diese Datei enthält einige ANR-Dumps sowie GC-Zeiten. Sie finden die GC-Zeiten, indem Sie nach Dumping cumulative Gc timings suchen. Diese Zeitangaben enthalten einige interessante Informationen, darunter die Histogrammdaten für die Phasen und Pausen der einzelnen GC-Typen. Die Pausen sind in der Regel wichtiger. Beispiel:

young concurrent copying paused:	Sum: 5.491ms 99% C.I. 1.464ms-2.133ms Avg: 1.830ms Max: 2.133ms

Die durchschnittliche Pause betrug 1,83 ms.Das sollte niedrig genug sein, um in den meisten Apps keine fehlenden Frames zu verursachen.

Ein weiterer interessanter Bereich ist die Zeit bis zur Sperrung. Damit wird gemessen, wie lange es dauert, bis ein Thread einen Sperrpunkt erreicht, nachdem der GC die Sperrung angefordert hat. Diese Zeit ist in den GC-Pausen enthalten. Daher ist es nützlich zu ermitteln, ob lange Pausen durch eine langsame GC oder eine langsame Aussetzung des Threads verursacht werden. Hier ein Beispiel für eine normale Zeit für die Sperrung auf einem Nexus 5:

suspend all histogram:	Sum: 1.513ms 99% C.I. 3us-546.560us Avg: 47.281us Max: 601us

Es gibt noch andere interessante Bereiche, darunter die Gesamtzeit und der GC-Durchsatz. Beispiele:

Total time spent in GC: 502.251ms
Mean GC size throughput: 92MB/s
Mean GC object throughput: 1.54702e+06 objects/s

Hier ein Beispiel dafür, wie Sie die GC-Zeiten einer bereits laufenden App dumpen:

adb shell kill -s QUIT PID
adb pull /data/anr/anr_2020-07-13-19-23-39-817

An diesem Punkt befinden sich die GC-Zeiten in anr_2020-07-13-19-23-39-817. Hier ein Beispiel für eine Ausgabe von Google Maps:

Start Dumping histograms for 2195 iterations for concurrent copying
MarkingPhase:   Sum: 258.127s 99% C.I. 58.854ms-352.575ms Avg: 117.651ms Max: 641.940ms
ScanCardsForSpace:      Sum: 85.966s 99% C.I. 15.121ms-112.080ms Avg: 39.164ms Max: 662.555ms
ScanImmuneSpaces:       Sum: 79.066s 99% C.I. 7.614ms-57.658ms Avg: 18.014ms Max: 546.276ms
ProcessMarkStack:       Sum: 49.308s 99% C.I. 6.439ms-81.640ms Avg: 22.464ms Max: 638.448ms
ClearFromSpace: Sum: 35.068s 99% C.I. 6.522ms-40.040ms Avg: 15.976ms Max: 633.665ms
SweepSystemWeaks:       Sum: 14.209s 99% C.I. 3.224ms-15.210ms Avg: 6.473ms Max: 201.738ms
CaptureThreadRootsForMarking:   Sum: 11.067s 99% C.I. 0.835ms-13.902ms Avg: 5.044ms Max: 25.565ms
VisitConcurrentRoots:   Sum: 8.588s 99% C.I. 1.260ms-8.547ms Avg: 1.956ms Max: 231.593ms
ProcessReferences:      Sum: 7.868s 99% C.I. 0.002ms-8.336ms Avg: 1.792ms Max: 17.376ms
EnqueueFinalizerReferences:     Sum: 3.976s 99% C.I. 0.691ms-8.005ms Avg: 1.811ms Max: 16.540ms
GrayAllDirtyImmuneObjects:      Sum: 3.721s 99% C.I. 0.622ms-6.702ms Avg: 1.695ms Max: 14.893ms
SweepLargeObjects:      Sum: 3.202s 99% C.I. 0.032ms-6.388ms Avg: 1.458ms Max: 549.851ms
FlipOtherThreads:       Sum: 2.265s 99% C.I. 0.487ms-3.702ms Avg: 1.031ms Max: 6.327ms
VisitNonThreadRoots:    Sum: 1.883s 99% C.I. 45us-3207.333us Avg: 429.210us Max: 27524us
InitializePhase:        Sum: 1.624s 99% C.I. 231.171us-2751.250us Avg: 740.220us Max: 6961us
ForwardSoftReferences:  Sum: 1.071s 99% C.I. 215.113us-2175.625us Avg: 488.362us Max: 7441us
ReclaimPhase:   Sum: 490.854ms 99% C.I. 32.029us-6373.807us Avg: 223.623us Max: 362851us
EmptyRBMarkBitStack:    Sum: 479.736ms 99% C.I. 11us-3202.500us Avg: 218.558us Max: 13652us
CopyingPhase:   Sum: 399.163ms 99% C.I. 24us-4602.500us Avg: 181.851us Max: 22865us
ThreadListFlip: Sum: 295.609ms 99% C.I. 15us-2134.999us Avg: 134.673us Max: 13578us
ResumeRunnableThreads:  Sum: 238.329ms 99% C.I. 5us-2351.250us Avg: 108.578us Max: 10539us
ResumeOtherThreads:     Sum: 207.915ms 99% C.I. 1.072us-3602.499us Avg: 94.722us Max: 14179us
RecordFree:     Sum: 188.009ms 99% C.I. 64us-312.812us Avg: 85.653us Max: 2709us
MarkZygoteLargeObjects: Sum: 133.301ms 99% C.I. 12us-734.999us Avg: 60.729us Max: 10169us
MarkStackAsLive:        Sum: 127.554ms 99% C.I. 13us-417.083us Avg: 58.111us Max: 1728us
FlipThreadRoots:        Sum: 126.119ms 99% C.I. 1.028us-3202.499us Avg: 57.457us Max: 11412us
SweepAllocSpace:        Sum: 117.761ms 99% C.I. 24us-400.624us Avg: 53.649us Max: 1541us
SwapBitmaps:    Sum: 56.301ms 99% C.I. 10us-125.312us Avg: 25.649us Max: 1475us
(Paused)GrayAllNewlyDirtyImmuneObjects: Sum: 33.047ms 99% C.I. 9us-49.931us Avg: 15.055us Max: 72us
(Paused)SetFromSpace:   Sum: 11.651ms 99% C.I. 2us-49.772us Avg: 5.307us Max: 71us
(Paused)FlipCallback:   Sum: 7.693ms 99% C.I. 2us-32us Avg: 3.504us Max: 32us
(Paused)ClearCards:     Sum: 6.371ms 99% C.I. 250ns-49753ns Avg: 207ns Max: 188000ns
Sweep:  Sum: 5.793ms 99% C.I. 1us-49.818us Avg: 2.639us Max: 93us
UnBindBitmaps:  Sum: 5.255ms 99% C.I. 1us-31us Avg: 2.394us Max: 31us
Done Dumping histograms
concurrent copying paused:      Sum: 315.249ms 99% C.I. 49us-1378.125us Avg: 143.621us Max: 7722us
concurrent copying freed-bytes: Avg: 34MB Max: 54MB Min: 2062KB
Freed-bytes histogram: 0:4,5120:5,10240:19,15360:69,20480:167,25600:364,30720:529,35840:405,40960:284,46080:311,51200:38
concurrent copying total time: 569.947s mean time: 259.657ms
concurrent copying freed: 1453160493 objects with total size 74GB
concurrent copying throughput: 2.54964e+06/s / 134MB/s  per cpu-time: 157655668/s / 150MB/s
Average major GC reclaim bytes ratio 0.486928 over 2195 GC cycles
Average major GC copied live bytes ratio 0.0894662 over 2199 major GCs
Cumulative bytes moved 6586367960
Cumulative objects moved 127490240
Peak regions allocated 376 (94MB) / 2048 (512MB)
Start Dumping histograms for 685 iterations for young concurrent copying
ScanCardsForSpace:      Sum: 26.288s 99% C.I. 8.617ms-77.759ms Avg: 38.377ms Max: 432.991ms
ProcessMarkStack:       Sum: 21.829s 99% C.I. 2.116ms-71.119ms Avg: 31.868ms Max: 98.679ms
ClearFromSpace: Sum: 19.420s 99% C.I. 5.480ms-50.293ms Avg: 28.351ms Max: 507.330ms
ScanImmuneSpaces:       Sum: 9.968s 99% C.I. 8.155ms-30.639ms Avg: 14.552ms Max: 46.676ms
SweepSystemWeaks:       Sum: 6.741s 99% C.I. 3.655ms-14.715ms Avg: 9.841ms Max: 22.142ms
GrayAllDirtyImmuneObjects:      Sum: 4.466s 99% C.I. 0.584ms-14.315ms Avg: 6.519ms Max: 24.355ms
FlipOtherThreads:       Sum: 3.672s 99% C.I. 0.631ms-16.630ms Avg: 5.361ms Max: 18.513ms
ProcessReferences:      Sum: 2.806s 99% C.I. 0.001ms-9.459ms Avg: 2.048ms Max: 11.951ms
EnqueueFinalizerReferences:     Sum: 1.857s 99% C.I. 0.424ms-8.609ms Avg: 2.711ms Max: 24.063ms
VisitConcurrentRoots:   Sum: 1.094s 99% C.I. 1.306ms-5.357ms Avg: 1.598ms Max: 6.831ms
SweepArray:     Sum: 711.032ms 99% C.I. 0.022ms-3.502ms Avg: 1.038ms Max: 7.307ms
InitializePhase:        Sum: 667.346ms 99% C.I. 303us-2643.749us Avg: 974.227us Max: 3199us
VisitNonThreadRoots:    Sum: 388.145ms 99% C.I. 103.911us-1385.833us Avg: 566.635us Max: 5374us
ThreadListFlip: Sum: 202.730ms 99% C.I. 18us-2414.999us Avg: 295.956us Max: 6780us
EmptyRBMarkBitStack:    Sum: 132.934ms 99% C.I. 8us-1757.499us Avg: 194.064us Max: 8495us
ResumeRunnableThreads:  Sum: 109.593ms 99% C.I. 6us-4719.999us Avg: 159.989us Max: 11106us
ResumeOtherThreads:     Sum: 86.733ms 99% C.I. 3us-4114.999us Avg: 126.617us Max: 19332us
ForwardSoftReferences:  Sum: 69.686ms 99% C.I. 14us-2014.999us Avg: 101.731us Max: 4723us
RecordFree:     Sum: 58.889ms 99% C.I. 0.500us-185.833us Avg: 42.984us Max: 769us
FlipThreadRoots:        Sum: 58.540ms 99% C.I. 1.034us-4314.999us Avg: 85.459us Max: 10224us
CopyingPhase:   Sum: 52.227ms 99% C.I. 26us-728.749us Avg: 76.243us Max: 2060us
ReclaimPhase:   Sum: 37.207ms 99% C.I. 7us-2322.499us Avg: 54.316us Max: 3826us
(Paused)GrayAllNewlyDirtyImmuneObjects: Sum: 23.859ms 99% C.I. 11us-98.917us Avg: 34.830us Max: 128us
FreeList:       Sum: 20.376ms 99% C.I. 2us-188.875us Avg: 29.573us Max: 998us
MarkZygoteLargeObjects: Sum: 18.970ms 99% C.I. 4us-115.749us Avg: 27.693us Max: 122us
(Paused)SetFromSpace:   Sum: 12.331ms 99% C.I. 3us-94.226us Avg: 18.001us Max: 109us
SwapBitmaps:    Sum: 11.761ms 99% C.I. 5us-49.968us Avg: 17.169us Max: 67us
ResetStack:     Sum: 4.317ms 99% C.I. 1us-64.374us Avg: 6.302us Max: 190us
UnBindBitmaps:  Sum: 3.803ms 99% C.I. 4us-49.822us Avg: 5.551us Max: 70us
(Paused)ClearCards:     Sum: 3.336ms 99% C.I. 250ns-7000ns Avg: 347ns Max: 7000ns
(Paused)FlipCallback:   Sum: 3.082ms 99% C.I. 1us-30us Avg: 4.499us Max: 30us
Done Dumping histograms
young concurrent copying paused:        Sum: 229.314ms 99% C.I. 37us-2287.499us Avg: 334.764us Max: 6850us
young concurrent copying freed-bytes: Avg: 44MB Max: 50MB Min: 9132KB
Freed-bytes histogram: 5120:1,15360:1,20480:6,25600:1,30720:1,35840:9,40960:235,46080:427,51200:4
young concurrent copying total time: 100.823s mean time: 147.187ms
young concurrent copying freed: 519927309 objects with total size 30GB
young concurrent copying throughput: 5.15683e+06/s / 304MB/s  per cpu-time: 333152554/s / 317MB/s
Average minor GC reclaim bytes ratio 0.52381 over 685 GC cycles
Average minor GC copied live bytes ratio 0.0512109 over 685 minor GCs
Cumulative bytes moved 1542000944
Cumulative objects moved 28393168
Peak regions allocated 376 (94MB) / 2048 (512MB)
Total time spent in GC: 670.771s
Mean GC size throughput: 159MB/s per cpu-time: 177MB/s
Mean GC object throughput: 2.94152e+06 objects/s
Total number of allocations 1974199562
Total bytes allocated 104GB
Total bytes freed 104GB
Free memory 10MB
Free memory until GC 10MB
Free memory until OOME 442MB
Total memory 80MB
Max memory 512MB
Zygote space size 2780KB
Total mutator paused time: 544.563ms
Total time waiting for GC to complete: 117.494ms
Total GC count: 2880
Total GC time: 670.771s
Total blocking GC count: 1
Total blocking GC time: 86.373ms
Histogram of GC count per 10000 ms: 0:259879,1:2828,2:24,3:1
Histogram of blocking GC count per 10000 ms: 0:262731,1:1
Native bytes total: 30599192 registered: 8947416
Total native bytes at last GC: 30344912

Tools zur Analyse von Problemen mit der Korrektheit der Garbage Collection

Es gibt verschiedene Ursachen für Abstürze in ART. Abstürze beim Lesen oder Schreiben in Objektfelder können auf eine Beschädigung des Heaps hinweisen. Wenn der GC während der Ausführung abstürzt, kann dies auch auf eine Beschädigung des Heaps hinweisen. Die häufigste Ursache für eine Beschädigung des Heaps ist fehlerhafter App-Code. Glücklicherweise gibt es Tools zur Behebung von GC- und Heap-bezogenen Abstürzen, einschließlich der oben genannten Optionen zur Überprüfung des Heaps und CheckJNI.

CheckJNI

CheckJNI ist ein Modus, der JNI-Prüfungen zum Überprüfen des App-Verhaltens hinzufügt. Diese sind aus Leistungsgründen standardmäßig nicht aktiviert. Die Prüfungen erfassen einige Fehler, die zu einer Beschädigung des Heaps führen können, z. B. die Verwendung ungültiger/veralteter lokaler und globaler Referenzen. So aktivieren Sie CheckJNI:

adb shell setprop dalvik.vm.checkjni true

Der Forcecopy-Modus von CheckJNI ist nützlich, um Schreibvorgänge zu erkennen, die über das Ende von Arrayregionen hinausgehen. Wenn diese Option aktiviert ist, geben die JNI-Funktionen für den Arrayzugriff Kopien mit roten Zonen zurück. Eine rote Zone ist eine Region am Ende oder Anfang des zurückgegebenen Zeigers mit einem speziellen Wert, der beim Freigeben des Arrays überprüft wird. Wenn die Werte im roten Bereich nicht den erwarteten Werten entsprechen, ist ein Pufferüberlauf oder -unterlauf aufgetreten. Dies führt dazu, dass CheckJNI abgebrochen wird. So aktivieren Sie den Modus „Forcecopy“:

adb shell setprop dalvik.vm.jniopts forcecopy

Ein Beispiel für einen Fehler, den CheckJNI erkennen sollte, ist das Schreiben über das Ende eines Arrays hinaus, das von GetPrimitiveArrayCritical abgerufen wurde. Dieser Vorgang kann den Java-Heap beschädigen. Wenn sich die Schreiboperation im roten Bereich von CheckJNI befindet, wird das Problem von CheckJNI erkannt, wenn die entsprechende ReleasePrimitiveArrayCritical aufgerufen wird. Andernfalls beschädigt der Schreibvorgang ein zufälliges Objekt im Java-Heap und kann zu einem zukünftigen GC-Absturz führen. Wenn der beschädigte Speicher ein Referenzfeld ist, kann die automatische Speicherbereinigung den Fehler erfassen und die Fehlermeldung Tried to mark <ptr> not contained by any spaces (Versuch, <ptr> als nicht in einem Leerraum enthalten zu markieren) ausgeben.

Dieser Fehler tritt auf, wenn der GC versucht, ein Objekt zu kennzeichnen, für das er keinen Speicherplatz finden kann. Wenn diese Prüfung fehlschlägt, durchsucht der GC die Stammobjekte und versucht zu ermitteln, ob das ungültige Objekt ein Stammobjekt ist. Es gibt zwei Möglichkeiten: Das Objekt ist ein Stamm- oder ein Nicht-Stammobjekt.

Beispiel für ungültigen Stamm

Wenn das Objekt ein ungültiger Stamm ist, werden einige nützliche Informationen ausgegeben: art E 5955 5955 art/runtime/gc/collector/mark_sweep.cc:383] Tried to mark 0x2 not contained by any spaces

art E  5955  5955 art/runtime/gc/collector/mark_sweep.cc:384] Attempting see if
it's a bad root
art E  5955  5955 art/runtime/gc/collector/mark_sweep.cc:485] Found invalid
root: 0x2
art E  5955  5955 art/runtime/gc/collector/mark_sweep.cc:486]
Type=RootJavaFrame thread_id=1 location=Visiting method 'java.lang.Object
com.google.gwt.collections.JavaReadableJsArray.get(int)' at dex PC 0x0002
(native PC 0xf19609d9) vreg=1

In diesem Fall sollte vreg=1 in com.google.gwt.collections.JavaReadableJsArray.get eine Heapreferenz enthalten, enthält aber einen ungültigen Pointer auf die Adresse 0x2. Das ist ein ungültiger Stamm. Verwenden Sie oatdump für die Oat-Datei, um dieses Problem zu beheben, und sehen Sie sich die Methode mit dem ungültigen Stamm an. In diesem Fall stellte sich heraus, dass der Fehler ein Compilerfehler im x86-Back-End war. Hier ist die Änderungsliste, mit der das Problem behoben wurde: https://android-review.googlesource.com/#/c/133932/

Beispiel für ein beschädigtes Objekt

Wenn das Objekt kein Stammobjekt ist, wird eine ähnliche Ausgabe wie die folgende ausgegeben:

01-15 12:38:00.196  1217  1238 E art     : Attempting see if it's a bad root
01-15 12:38:00.196  1217  1238 F art     :
art/runtime/gc/collector/mark_sweep.cc:381] Can't mark invalid object

Wenn die Beschädigung des Heaps nicht auf einen ungültigen Ursprung zurückzuführen ist, ist es schwierig, sie zu beheben. Diese Fehlermeldung gibt an, dass sich mindestens ein Objekt im Heap auf das ungültige Objekt bezog.