Repository: square/leakcanary
Issue: Oom analyzing leaks

### Steps to Reproduce

Will upload hprof privately.

### Version Information

* LeakCanary version: 2.7
* Android OS version: 12
* Gradle version: 7.0

### Additional Information

```
java.lang.RuntimeException: Not enough memory to allocate buffers for rehashing: 2097152 -> 4194304
	at shark.internal.hppc.LongLongScatterMap.allocateBuffers(LongLongScatterMap.kt:329)
	at shark.internal.hppc.LongLongScatterMap.allocateThenInsertThenRehash(LongLongScatterMap.kt:361)
	at shark.internal.hppc.LongLongScatterMap.set(LongLongScatterMap.kt:106)
	at shark.internal.DominatorTree.updateDominated(DominatorTree.kt:51)
	at shark.internal.PathFinder$VisitTracker$Dominated.visited(PathFinder.kt:94)
	at shark.internal.PathFinder.enqueue(PathFinder.kt:630)
	at shark.internal.PathFinder.visitInstance(PathFinder.kt:502)
	at shark.internal.PathFinder.findPathsFromGcRoots(PathFinder.kt:277)
	at shark.internal.PathFinder.findPathsFromGcRoots(PathFinder.kt:225)
	at shark.HeapAnalyzer.findLeaks(HeapAnalyzer.kt:197)
	at shark.HeapAnalyzer.analyzeGraph(HeapAnalyzer.kt:175)
	at shark.HeapAnalyzer.analyze(HeapAnalyzer.kt:101)
	at leakcanary.internal.HeapAnalyzerService.analyzeHeap(HeapAnalyzerService.kt:87)
	at leakcanary.internal.HeapAnalyzerService.onHandleIntentInForeground(HeapAnalyzerService.kt:61)
	at leakcanary.internal.ForegroundService.onHandleIntent(ForegroundService.kt:55)
	at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:78)
	at android.os.Handler.dispatchMessage(Handler.java:106)
	at android.os.Looper.loopOnce(Looper.java:201)
	at android.os.Looper.loop(Looper.java:288)
	at android.os.HandlerThread.run(HandlerThread.java:67)
Caused by: java.lang.OutOfMemoryError: Failed to allocate a 33554456 byte allocation with 21859432 free bytes and 20MB until OOM, target footprint 268435456, growth limit 268435456
	at shark.internal.hppc.LongLongScatterMap.allocateBuffers(LongLongScatterMap.kt:324)
	... 19 more

Build.VERSION.SDK_INT: 31
Build.MANUFACTURER: Google
LeakCanary version: 2.7
Analysis duration: 53096 ms
```

Changed files:
leakcanary-android-core/src/main/java/leakcanary/internal/HeapAnalyzerService.kt

diff --git a/leakcanary-android-core/src/main/java/leakcanary/internal/HeapAnalyzerService.kt b/leakcanary-android-core/src/main/java/leakcanary/internal/HeapAnalyzerService.kt
index 8aef5e8bc..ded273196 100644
--- a/leakcanary-android-core/src/main/java/leakcanary/internal/HeapAnalyzerService.kt
+++ b/leakcanary-android-core/src/main/java/leakcanary/internal/HeapAnalyzerService.kt
@@ -67,7 +67,23 @@ internal class HeapAnalyzerService : ForegroundService(
         dumpDurationMillis = heapDumpDurationMillis,
         metadata = heapAnalysis.metadata + ("Heap dump reason" to heapDumpReason)
       )
-      is HeapAnalysisFailure -> heapAnalysis.copy(dumpDurationMillis = heapDumpDurationMillis)
+      is HeapAnalysisFailure -> {
+        val failureCause = heapAnalysis.exception.cause!!
+        if (failureCause is OutOfMemoryError) {
+          heapAnalysis.copy(
+            dumpDurationMillis = heapDumpDurationMillis,
+            exception = HeapAnalysisException(RuntimeException("""
+              Not enough memory to analyze heap. You can:
+              - Kill the app then restart the analysis from the LeakCanary activity.
+              - Increase the memory available to your debug app with largeHeap=true: https://developer.android.com/guide/topics/manifest/application-element#largeHeap
+              - Set up LeakCanary to run in a separate process: https://square.github.io/leakcanary/recipes/#running-the-leakcanary-analysis-in-a-separate-process
+              - Download the heap dump from the LeakCanary activity then run the analysis from your computer with shark-cli: https://square.github.io/leakcanary/shark/#shark-cli
+            """.trimIndent(), failureCause))
+          )
+        } else {
+          heapAnalysis.copy(dumpDurationMillis = heapDumpDurationMillis)
+        }
+      }
     }
     onAnalysisProgress(REPORTING_HEAP_ANALYSIS)
     config.onHeapAnalyzedListener.onHeapAnalyzed(fullHeapAnalysis)