ART 가비지 컬렉션 디버그

이 페이지에서는 Android 런타임(ART) 가비지 컬렉션(GC) 정확성 및 성능 문제를 디버그하는 방법을 설명합니다. GC 확인 옵션을 사용하고 GC 확인 실패에 관한 해결 방법을 파악하며 GC 성능 문제를 측정 및 처리하는 방법이 설명되어 있습니다.

ART를 사용하려면 이 ART 및 Dalvik 섹션의 페이지와 Dalvik Executable 형식을 참고하세요. 앱 동작 확인에 관한 추가적인 도움말은 Android 런타임(ART)에서 앱 동작 확인을 참고하세요.

ART GC 개요

ART에는 각기 다른 가비지 컬렉터를 실행하는 여러 GC 계획이 있습니다. Android 8(Oreo)부터 기본 계획은 동시 복사(CC)입니다. 다른 GC 계획은 동시 마크 스윕(CMS)입니다.

동시 복사 GC의 몇 가지 주요 특징은 다음과 같습니다.

  • CC는 RegionTLAB라는 범프 포인터 할당자를 사용 가능하게 합니다. 이 할당자는 스레드 로컬 할당 버퍼(TLAB)를 각 앱 스레드에 할당합니다. 그런 다음, 동기화하지 않고 단순히 '최상위' 포인터를 범프하여 TLAB에서 객체를 할당할 수 있습니다.
  • CC는 앱 스레드를 일시중지하지 않고 동시에 객체를 복사하여 힙의 디스크 조각 모음을 실행합니다. 이 작업은 앱 개발자의 개입 없이 힙에서 참조 읽기를 가로채는 읽기 배리어를 사용하여 실행됩니다.
  • GC에는 한 번의 작은 일시중지만 있으며, 이는 힙 크기와 관련한 경우 시간이 일정합니다.
  • CC는 Android 10 이상에서 세대 기반 GC로 확장됩니다. 생성된 지 얼마 안 된 객체를 수집할 수 있으며 이러한 객체는 별다른 노력 없이도 상당히 빨리 도달할 수 없는 상태가 됩니다. 이는 GC 처리량을 늘리고 전체 힙 GC를 실행할 필요를 크게 지연시킵니다.

ART에서 계속 지원하는 다른 GC는 CMS입니다. 이 GC는 압축도 지원하지만 동시에 지원하지는 않습니다. 앱이 백그라운드로 전환될 때까지 압축은 발생하지 않으며 앱이 백그라운드로 전환될 때 앱 스레드가 일시중지되어 압축을 실행합니다. 또한, 단편화로 인해 객체 할당이 실패할 때도 압축을 실행해야 합니다. 이 경우 앱이 일정 시간 동안 응답하지 않을 수 있습니다.

CMS는 거의 압축을 하지 않으므로 사용 가능한 객체가 인접하지 않을 수 있기 때문에 이러한 객체는 사용 가능 목록을 기반으로 하는 RosAlloc 할당자를 사용합니다. 할당 비용은 RegionTLAB보다 높습니다. 마지막으로, 내부 단편화로 인해 Java 힙의 메모리 사용량이 CC보다 CMS에서 더 높을 수 있습니다.

GC 확인 및 성능 옵션

GC 유형 변경

OEM은 GC 유형을 변경할 수 있습니다. 변경 프로세스에는 빌드 시 ART_USE_READ_BARRIER 환경 변수 설정이 포함됩니다. 기본값은 true이며, 이 값을 사용하면 CC 컬렉터에서 읽기 배리어를 사용하므로 CC 컬렉터를 사용할 수 있습니다. CMS의 경우, 이 변수를 false로 명시하여 설정해야 합니다.

기본적으로 CC 컬렉터는 Android 10 이상에서 세대 기반 모드로 실행됩니다. 세대 기반 모드를 사용 중지하려면 -Xgc:nogenerational_cc 명령줄 인수를 사용하면 됩니다. 또는 시스템 속성을 다음과 같이 설정할 수 있습니다.

adb shell setprop dalvik.vm.gctype nogenerational_cc
CMS 컬렉터는 항상 세대 기반 모드로 실행됩니다.

힙 확인

힙 확인은 아마도 GC 관련 오류나 힙 손상을 디버그하는 데 가장 유용한 GC 옵션일 것입니다. 힙 확인을 사용 설정하면 GC에서 가비지 컬렉션 프로세스 도중 몇 개의 지점에서 힙의 정확성을 검사합니다. 힙 확인은 GC 유형을 변경하는 것과 동일한 옵션을 공유합니다. 힙 확인이 사용 설정되면 루트를 확인하고 도달 가능한 객체가 다른 도달 가능한 객체만 참조하도록 합니다. 다음 -Xgc 값을 전달하면 GC 확인이 사용 설정됩니다.

  • [no]preverify를 사용 설정하면 GC를 시작하기 전에 힙 확인을 실행합니다.
  • [no]presweepingverify를 사용 설정하면 가비지 컬렉터 스위핑 프로세스를 시작하기 전에 힙 확인을 실행합니다.
  • [no]postverify를 사용 설정하면 GC에서 스위핑을 완료한 후 힙 확인을 실행합니다.
  • [no]preverify_rosalloc, [no]postsweepingverify_rosalloc, [no]postverify_rosalloc은 추가적인 GC 옵션으로 RosAlloc의 내부 어카운팅 상태만 확인합니다. 따라서 RosAlloc 할당자를 사용하는 CMS 컬렉터에만 적용할 수 있습니다. 매직 값이 예상 상수와 일치하고 메모리의 여유 블록은 free_page_runs_ 맵에 모두 등록되었는지를 주요하게 확인합니다.

성능

GC 성능, GC 타이밍 덤프 및 Systrace를 측정하는 두 가지 기본 도구가 있습니다. Perfetto라는 Systrace의 고급 버전도 있습니다. GC 성능 문제를 측정하는 시각적 방법은 Systrace 및 Perfetto를 사용하여 긴 일시중지를 유발하거나 앱 스레드를 선점하는 GC를 파악하는 것입니다. 시간이 지나면서 ART GC가 크게 개선되었지만, 과도한 할당과 같은 잘못된 뮤테이터 동작으로 인해 여전히 성능 문제가 발생할 수 있습니다.

컬렉션 전략

CC GC는 Young GC 또는 전체 힙 GC 중 하나를 실행하여 수집합니다. Young GC를 더 자주 실행하는 것이 좋습니다. GC는 방금 완료된 컬렉션 주기의 처리량(해제된 바이트를 GC 지속 시간(초)으로 나누어 계산)이 전체 힙 CC 컬렉션의 평균 처리량보다 작을 때까지 Young CC 컬렉션을 실행합니다. 이 경우, Young CC 대신 전체 힙 CC가 다음 동시 GC로 선택됩니다. 전체 힙 컬렉션이 완료되면 다음 GC는 Young CC로 다시 전환됩니다. 이 전략이 작동하는 한 가지 주요 요소는 CC가 완료된 후 Young CC에서 힙 공간 한도를 조정하지 않는 것입니다. 이로 인해 처리량이 전체 힙 CC보다 낮아질 때까지 Young CC가 더 자주 발생하며 그 결과 힙이 증가합니다.

SIGQUIT를 사용하여 GC 성능 정보 가져오기

앱의 GC 성능 타이밍을 가져오려면 명령줄 프로그램을 시작할 때 SIGQUIT을 이미 실행 중인 앱으로 전송하거나 -XX:DumpGCPerformanceOnShutdowndalvikvm으로 전달합니다. 앱이 ANR 요청 신호(SIGQUIT)를 가져오면 잠금, 스레드 스택 및 GC 성능과 관련된 정보를 덤프합니다.

GC 타이밍 덤프를 가져오려면 다음을 사용하세요.

adb shell kill -s QUIT PID

그러면 이름에 날짜와 시간이 포함된 파일(예: anr_2020-07-13-19-23-39-817)이 /data/anr/에 생성됩니다. 이 파일에는 GC 타이밍뿐 아니라 일부 ANR 덤프가 포함되어 있습니다. 누적 GC 타이밍 덤프를 검색하여 GC 타이밍을 찾을 수 있습니다. 이러한 타이밍으로 각 GC 유형의 단계 및 일시중지에 관한 히스토그램 정보 등 흥미로울 수 있는 몇 가지 정보를 확인할 수 있습니다. 대개 일시중지가 더 중요한 정보입니다. 예:

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

이는 평균 일시중지가 1.83ms였음을 보여 주며 이 정도로는 대부분의 앱에서 프레임 누락이 발생하지 않기 때문에 문제가 되지 않습니다.

또 다른 관심 영역은 정지 시간으로, GC에서 정지를 요청한 후 스레드가 정지 시점에 도달하는 데 걸리는 시간을 측정합니다. 이 시간은 GC 일시중지에 포함되므로 긴 일시중지가 GC가 느려서인지 또는 스레드가 느리게 정지되어서인지 확인하는 데 유용합니다. 다음은 Nexus 5에서 일반적인 정지 시간의 예입니다.

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

총 소요 시간 및 GC 처리량을 비롯하여 다른 관심 영역이 있습니다. 예를 들면 다음과 같습니다.

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

다음은 이미 실행 중인 앱의 GC 타이밍을 덤프하는 방법의 예입니다.

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

이 시점에서 GC 타이밍은 anr_2020-07-13-19-23-39-817 내부에 있습니다. 다음은 Google 지도의 출력 예입니다.

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

GC 정확성 문제를 분석하는 데 사용되는 도구

ART 내부에서 비정상 종료를 일으키는 원인은 여러 가지가 있을 수 있습니다. 객체 필드를 읽거나 객체 필드에 쓸 때 비정상 종료가 발생한다면 힙이 손상되었을 가능성이 있습니다. GC가 실행 중에 비정상 종료되는 경우에도 힙 손상이 원인일 수 있습니다. 힙 손상이 발생하는 가장 일반적인 원인은 잘못된 앱 코드입니다. 다행히 GC 및 힙 관련 비정상 종료를 디버그하는 도구가 있습니다. 예를 들면 위에서 지정한 힙 확인 옵션과 CheckJNI입니다.

CheckJNI

CheckJNI는 JNI 검사를 추가하여 앱 동작을 확인하는 모드입니다. 이 기능은 성능상의 이유로 기본적으로 사용 설정되지 않습니다. 이 검사는 무효하거나 비활성인 로컬 및 글로벌 참조 사용과 같이 힙 손상을 일으킬 수 있는 오류를 포착합니다. CheckJNI를 사용 설정하는 방법은 다음과 같습니다.

adb shell setprop dalvik.vm.checkjni true

CheckJNI의 forcecopy 모드는 배열 영역의 끝을 넘어선 쓰기를 감지하는 데 유용합니다. forcecopy가 사용 설정되면 배열 액세스 JNI 함수가 레드존이 있는 사본을 반환합니다. 레드존은 특별한 값이 있는 반환된 포인터의 끝 또는 시작 영역으로, 배열이 해제될 때 확인됩니다. 레드존의 값이 예상한 것과 일치하지 않는다면 버퍼 오버런 또는 언더런이 발생한 것입니다. 이로 인해 CheckJNI가 취소됩니다. forcecopy 모드를 사용 설정하는 방법은 다음과 같습니다.

adb shell setprop dalvik.vm.jniopts forcecopy

CheckJNI가 포착해야 하는 오류의 예는 GetPrimitiveArrayCritical에서 획득한 배열의 끝을 넘어선 쓰기입니다. 이 작업으로 Java 힙이 손상될 수 있습니다. 이러한 쓰기가 CheckJNI 레드존 내에 있는 경우 CheckJNI는 상응하는 ReleasePrimitiveArrayCritical이 호출될 때 이 문제를 포착합니다. 그렇지 않은 경우 쓰기가 Java 힙에 있는 임의의 객체를 손상하여 향후 GC 비정상 종료를 유발할 수 있습니다. 손상된 메모리가 참조 필드라면 GC는 오류를 포착하고 어떤 공간에도 포함되지 않는 <ptr>을 표시하려고 시도함 오류를 출력합니다.

이 오류는 GC에서 공간을 찾을 수 없는 객체를 표시하려고 할 때 발생합니다. 이 검사가 실패하면 GC는 루트를 탐색하고 무효한 객체가 루트인지 확인합니다. 여기에는 객체가 루트이거나 루트 외 객체라는 두 가지 옵션이 있습니다.

무효한 루트의 예

객체가 무효한 루트인 경우 다음과 같은 유용한 정보를 출력합니다. 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

이 경우 com.google.gwt.collections.JavaReadableJsArray.get 내부의 vreg=1은 힙 참조를 포함해야 하지만 주소 0x2의 무효한 포인터가 포함되어 있습니다. 이는 무효한 루트입니다. 이 문제를 디버그하려면 oat 파일에서 oatdump를 사용하여 무효한 루트가 있는 메서드를 확인합니다. 이 경우 오류는 x86 백엔드의 컴파일러 버그로 확인되었습니다. 다음은 이 오류를 수정한 변경 목록입니다. https://android-review.googlesource.com/#/c/133932/

손상된 객체의 예

객체가 루트가 아니라면 출력은 다음과 유사합니다.

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

힙 손상이 무효한 루트가 아니면 디버그하기가 어렵습니다. 이 오류 메시지는 힙에 무효한 객체를 가리키는 객체가 하나 이상 있음을 의미합니다.