Improving perf
diff --git a/leakcanary-android-instrumentation/src/androidTest/java/leakcanary/Api23Test.kt b/leakcanary-android-instrumentation/src/androidTest/java/leakcanary/Api23Test.kt index a038763..5840d1b 100644 --- a/leakcanary-android-instrumentation/src/androidTest/java/leakcanary/Api23Test.kt +++ b/leakcanary-android-instrumentation/src/androidTest/java/leakcanary/Api23Test.kt
@@ -4,13 +4,17 @@ import androidx.test.platform.app.InstrumentationRegistry import java.io.File import java.io.FileOutputStream +import leakcanary.internal.friendly.measureDurationMillis import org.junit.Test import shark.AndroidMetadataExtractor import shark.AndroidObjectInspectors import shark.AndroidReferenceMatchers +import shark.DualSourceProvider +import shark.FileSourceProvider import shark.HeapAnalyzer import shark.HprofHeapGraph.Companion.openHeapGraph import shark.KeyedWeakReferenceFinder +import shark.RandomAccessFileSourceProvider import shark.SharkLog class Api23Test { @@ -28,9 +32,22 @@ context.assets.open("api_23_dump.hprof") .copyTo(FileOutputStream(heapDumpFile)) + + GcTrigger.Default.runGc() + val fileChannelDuration = measureDurationMillis { + FileSourceProvider(heapDumpFile).runAnalysis() + } + GcTrigger.Default.runGc() + val randomAccessFileDuration = measureDurationMillis { + RandomAccessFileSourceProvider(heapDumpFile).runAnalysis() + } + SharkLog.d { "fileChannelDuration: $fileChannelDuration ms randomAccessFileDuration: $randomAccessFileDuration ms" } + } + + private fun DualSourceProvider.runAnalysis() { Trace.beginSection("Analysis") Trace.beginSection("Open graph ") - heapDumpFile.openHeapGraph() + openHeapGraph() .use { graph -> val analyzer = HeapAnalyzer { Trace.endSection() @@ -39,7 +56,7 @@ } val result = analyzer.analyze( - heapDumpFile = heapDumpFile, + heapDumpFile = File("who_cares"), graph = graph, leakingObjectFinder = KeyedWeakReferenceFinder, referenceMatchers = AndroidReferenceMatchers.appDefaults,
diff --git a/shark-hprof/src/main/java/shark/RandomAccessFileSourceProvider.kt b/shark-hprof/src/main/java/shark/RandomAccessFileSourceProvider.kt new file mode 100644 index 0000000..3c54b15 --- /dev/null +++ b/shark-hprof/src/main/java/shark/RandomAccessFileSourceProvider.kt
@@ -0,0 +1,54 @@ +package shark + +import java.io.File +import java.io.RandomAccessFile +import kotlin.math.min +import okio.Buffer +import okio.BufferedSource +import okio.Okio + +class RandomAccessFileSourceProvider(private val file: File) : DualSourceProvider { + override fun openStreamingSource(): BufferedSource = Okio.buffer(Okio.source(file.inputStream())) + + override fun openRandomAccessSource(): RandomAccessSource { + + val randomAccessFile = RandomAccessFile(file, "r") + + val arrayBuffer = ByteArray(500_000) + + return object : RandomAccessSource { + override fun read( + sink: Buffer, + position: Long, + byteCount: Long + ): Long { + val byteCountInt = byteCount.toInt() + randomAccessFile.seek(position) + var totalBytesRead = 0 + val maxRead = arrayBuffer.size + var toRead: Int = min(byteCountInt, maxRead) + while (totalBytesRead < byteCount) { + val bytesRead = randomAccessFile.read(arrayBuffer, 0, toRead) + if (bytesRead == -1) { + check(totalBytesRead != 0) { + "Didn't read nothing" + } + break + } + sink.write(arrayBuffer, 0, bytesRead) + totalBytesRead += bytesRead + toRead = min(byteCountInt - totalBytesRead, maxRead) + } + return totalBytesRead.toLong() + } + + override fun close() { + try { + randomAccessFile.close() + } catch (ignored: Throwable) { + SharkLog.d(ignored) { "Failed to close file, ignoring" } + } + } + } + } +}