Shark 🦈¶
Shark:Smart Heap Analysis Reports for Kotlin
Shark 是为 LeakCanary 2 提供支持的堆分析器。它是一个 Kotlin 独立堆分析库,具有高速和低内存占用的特点。
Shark 分层发布
- Shark Hprof:读取和写入 hprof 文件中的记录。
- Shark Graph:遍历堆对象图。
- Shark:生成堆分析报告。
- Shark Android:用于生成定制堆分析报告的 Android 启发式算法。
- Shark CLI:分析连接到你桌面的 Android 设备上已安装的可调试应用的堆。其输出类似于 LeakCanary 的输出,但你无需将 LeakCanary 依赖项添加到你的应用中。
- LeakCanary:在此基础上构建。它会自动监视已销毁的 Activity 和 Fragment,触发堆转储,运行 Shark Android,然后显示结果。
更多内容
- Shark 构建在 Okio 之上。Okio 使高效解析堆转储成为可能。
- Shark 是一个 100% Kotlin 库,Kotlin 对其设计至关重要,因为 Shark 大量依赖密封类和序列来节省内存。
- Shark 具有独特的能力,可以通过特定于平台的启发式算法帮助缩小内存泄漏的原因范围。
- Shark 经过严格测试(80% 测试覆盖率)。
- Shark 可以在 Java 和 Android 虚拟机中运行,除了 Okio 和 Kotlin,没有其他依赖。
- Shark 可以分析 Java 和 Android 虚拟机生成的 hprof 文件。
- 如果可以访问混淆映射文件,Shark 可以对 hprof 记录进行去混淆。
Shark CLI¶
Shark 命令行界面(CLI)使你能够直接从电脑上分析堆。它可以转储连接的 Android 设备上已安装应用的堆,对其进行分析,甚至可以从堆转储中去除任何敏感数据(例如个人身份信息、密码或加密密钥),这在共享堆转储时非常有用。
通过 Homebrew 安装
brew install leakcanary-shark
你也可以在此下载。
然后,你可以在任何连接的设备上的应用中查找内存泄漏,例如
$ shark-cli --device emulator-5554 --process com.example.app.debug analyze
信息
shark-cli
适用于所有可调试应用,即使它们不包含 leakcanary-android
依赖项。
运行 shark-cli
查看使用说明
$ shark-cli
Usage: shark-cli [OPTIONS] COMMAND [ARGS]...
^`. .=""=.
^_ \ \ / _ _ \
\ \ { \ | d b |
{ \ / `~~~--__ \ /\ /
{ \___----~~' `~~-_/'-=\/=-'\,
\ /// a `~. \ \
/ /~~~~-, ,__. , /// __,,,,) \ |
\/ \/ `~~~; ,---~~-_`/ \ / \/
/ / '. .'
'._.' _|`~~`|_
/|\ /|\
Options:
-p, --process TEXT Full or partial name of a process, e.g.
"example" would match "com.example.app"
-d, --device ID device/emulator id
-m, --obfuscation-mapping PATH path to obfuscation mapping file
--verbose / --no-verbose provide additional details as to what
shark-cli is doing
-h, --hprof FILE path to a .hprof file
--help Show this message and exit
Commands:
interactive Explore a heap dump.
analyze Analyze a heap dump.
dump-process Dump the heap and pull the hprof file.
strip-hprof Replace all primitive arrays from the provided heap dump with
arrays of zeroes and generate a new "-stripped.hprof" file.
Shark 代码示例¶
读取 hprof 文件中的记录¶
// build.gradle
dependencies {
implementation 'com.squareup.leakcanary:shark-hprof:$sharkVersion'
}
import java.io.File
import shark.Hprof
import shark.HprofRecord.StringRecord
import shark.OnHprofRecordListener
fun main(args: Array<String>) {
val heapDumpFile = File(args[0])
// Prints all class and field names
Hprof.open(heapDumpFile).use { hprof ->
hprof.reader.readHprofRecords(
recordTypes = setOf(StringRecord::class),
listener =
OnHprofRecordListener { position, record -> println((record as StringRecord).string) },
)
}
}
遍历堆对象图¶
// build.gradle
dependencies {
implementation 'com.squareup.leakcanary:shark-graph:$sharkVersion'
}
import java.io.File
import shark.Hprof
import shark.HprofHeapGraph
fun main(args: Array<String>) {
val heapDumpFile = File(args[0])
// Prints all thread names
Hprof.open(heapDumpFile).use { hprof ->
val heapGraph = HprofHeapGraph.indexHprof(hprof)
val threadClass = heapGraph.findClassByName("java.lang.Thread")!!
val threadNames: Sequence<String> =
threadClass.instances.map { instance ->
val nameField = instance["java.lang.Thread", "name"]!!
nameField.value.readAsJavaString()!!
}
threadNames.forEach { println(it) }
}
}
生成堆分析报告¶
// build.gradle
dependencies {
implementation 'com.squareup.leakcanary:shark:$sharkVersion'
}
import java.io.File
import shark.FilteringLeakingObjectFinder
import shark.FilteringLeakingObjectFinder.LeakingObjectFilter
import shark.HeapAnalyzer
import shark.HeapObject
import shark.HeapObject.HeapInstance
import shark.Hprof
import shark.HprofHeapGraph
// Marks any instance of com.example.ThingWithLifecycle with
// ThingWithLifecycle.destroyed=true as leaking
val leakingObjectFilter =
object : LeakingObjectFilter {
override fun isLeakingObject(heapObject: HeapObject): Boolean {
return if (
heapObject is HeapInstance && heapObject instanceOf "com.example.ThingWithLifecycle"
) {
val destroyedField = heapObject["com.example.ThingWithLifecycle", "destroyed"]!!
destroyedField.value.asBoolean!!
} else false
}
}
val leakingObjectFinder = FilteringLeakingObjectFinder(listOf(leakingObjectFilter))
fun main(args: Array<String>) {
val heapDumpFile = File(args[0])
val heapAnalysis =
Hprof.open(heapDumpFile).use { hprof ->
val heapGraph = HprofHeapGraph.indexHprof(hprof)
val heapAnalyzer = HeapAnalyzer({})
heapAnalyzer.analyze(
heapDumpFile = heapDumpFile,
graph = heapGraph,
leakingObjectFinder = leakingObjectFinder,
)
}
println(heapAnalysis)
}
生成 Android 堆分析报告¶
// build.gradle
dependencies {
implementation 'com.squareup.leakcanary:shark-android:$sharkVersion'
}
import java.io.File
import shark.AndroidObjectInspectors
import shark.AndroidReferenceMatchers
import shark.FilteringLeakingObjectFinder
import shark.FilteringLeakingObjectFinder.LeakingObjectFilter
import shark.HeapAnalyzer
import shark.HeapObject
import shark.HeapObject.HeapInstance
import shark.Hprof
import shark.HprofHeapGraph
// Marks any instance of com.example.ThingWithLifecycle with
// ThingWithLifecycle.destroyed=true as leaking
val leakingObjectFilter =
object : LeakingObjectFilter {
override fun isLeakingObject(heapObject: HeapObject): Boolean {
return if (
heapObject is HeapInstance && heapObject instanceOf "com.example.ThingWithLifecycle"
) {
val instance = heapObject as HeapInstance
val destroyedField = instance["com.example.ThingWithLifecycle", "destroyed"]!!
destroyedField.value.asBoolean!!
} else false
}
}
val leakingObjectFinder = FilteringLeakingObjectFinder(listOf(leakingObjectFilter))
fun main(args: Array<String>) {
val heapDumpFile = File(args[0])
val heapAnalysis =
Hprof.open(heapDumpFile).use { hprof ->
val heapGraph = HprofHeapGraph.indexHprof(hprof)
val heapAnalyzer = HeapAnalyzer({})
heapAnalyzer.analyze(
heapDumpFile = heapDumpFile,
graph = heapGraph,
leakingObjectFinder = leakingObjectFinder,
referenceMatchers = AndroidReferenceMatchers.appDefaults,
objectInspectors = AndroidObjectInspectors.appDefaults,
)
}
println(heapAnalysis)
}