15.ObjectInspectors 一组默认的 [ObjectInspector] ,用于查看常见的 JDK 对象的信息。KEYED_WEAK_REFERENCE的leakingObjectFilter用于过滤泄漏的对象
/**
* A set of default [ObjectInspector]s that knows about common JDK objects.
* 一组默认的 [ObjectInspector] ,用于查看常见的 JDK 对象的信息。
*/
enum class ObjectInspectors : ObjectInspector {
KEYED_WEAK_REFERENCE {
override val leakingObjectFilter = { heapObject: HeapObject ->
KeyedWeakReferenceFinder.findKeyedWeakReferences(heapObject.graph)
.filter { it.hasReferent && it.isRetained }//filter返回一个list,条件为true的
.any { reference ->//至少有一个
reference.referent.value == heapObject.objectId
}
}
override fun inspect(
reporter: ObjectReporter
) {
val graph = reporter.heapObject.graph//HeapGraph堆中的对象图,可用于导航堆。
val references = KeyedWeakReferenceFinder.findKeyedWeakReferences(graph)//返回所有的KeyedWeakReference类实例
val objectId = reporter.heapObject.objectId
references.forEach { ref ->
if (ref.referent.value == objectId) {//如果和reporter的heapObject的objectId一样的话
reporter.leakingReasons += if (ref.description.isNotEmpty()) {
"ObjectWatcher was watching this because ${ref.description}"
} else {
"ObjectWatcher was watching this"
}
reporter.labels += "key = ${ref.key}"
if (ref.watchDurationMillis != null) {
reporter.labels += "watchDurationMillis = ${ref.watchDurationMillis}"
}
if (ref.retainedDurationMillis != null) {
reporter.labels += "retainedDurationMillis = ${ref.retainedDurationMillis}"
}
}
}
}
},
CLASSLOADER {
override fun inspect(
reporter: ObjectReporter
) {
reporter.whenInstanceOf(ClassLoader::class) {
notLeakingReasons += "A ClassLoader is never leaking"
}
}
},
CLASS {
override fun inspect(
reporter: ObjectReporter
) {
if (reporter.heapObject is HeapClass) {
reporter.notLeakingReasons += "a class is never leaking"
}
}
},
ANONYMOUS_CLASS {
override fun inspect(
reporter: ObjectReporter
) {
val heapObject = reporter.heapObject
if (heapObject is HeapInstance) {
val instanceClass = heapObject.instanceClass
if (instanceClass.name.matches(ANONYMOUS_CLASS_NAME_PATTERN_REGEX)) {
val parentClassRecord = instanceClass.superclass!!
if (parentClassRecord.name == "java.lang.Object") {
try {
// This is an anonymous class implementing an interface. The API does not give access
// to the interfaces implemented by the class. We check if it's in the class path and
// use that instead.
// 这是一个实现接口的匿名类。 API 不提供对类实现的接口的访问。 我们检查它是否在类路径中并使用它。
val actualClass = Class.forName(instanceClass.name)
val interfaces = actualClass.interfaces
reporter.labels += if (interfaces.isNotEmpty()) {
val implementedInterface = interfaces[0]
"Anonymous class implementing ${implementedInterface.name}"
} else {
"Anonymous subclass of java.lang.Object"
}
} catch (ignored: ClassNotFoundException) {
}
} else {
// Makes it easier to figure out which anonymous class we're looking at.
//更容易找出我们正在查看的匿名类。
reporter.labels += "Anonymous subclass of ${parentClassRecord.name}"
}
}
}
}
},
THREAD {
override fun inspect(
reporter: ObjectReporter
) {
reporter.whenInstanceOf(Thread::class) { instance ->
/**
* 这里调用的是 [HeapInstance] 的operator fun get方法
*/
val threadName = instance[Thread::class, "name"]!!.value.readAsJavaString()
labels += "Thread name: '$threadName'"
}
}
};
internal open val leakingObjectFilter: ((heapObject: HeapObject) -> Boolean)? = null
companion object {
private const val ANONYMOUS_CLASS_NAME_PATTERN = "^.+\\$\\d+$"//内部类, ...$1.class
private val ANONYMOUS_CLASS_NAME_PATTERN_REGEX = ANONYMOUS_CLASS_NAME_PATTERN.toRegex()//正则
/** @see ObjectInspectors */
val jdkDefaults: List<ObjectInspector>
get() {
return values().toList()
}
/**
* Returns a list of [LeakingObjectFilter] suitable for common JDK projects.
*/
val jdkLeakingObjectFilters: List<LeakingObjectFilter> =
createLeakingObjectFilters(EnumSet.allOf(ObjectInspectors::class.java))
/**
* Creates a list of [LeakingObjectFilter] based on the passed in [ObjectInspectors].
*/
fun createLeakingObjectFilters(inspectors: Set<ObjectInspectors>): List<LeakingObjectFilter> =
inspectors.mapNotNull { it.leakingObjectFilter }
.map { filter ->
object : LeakingObjectFilter {
//目前看来只有KEYED_WEAK_REFERENCE有这个
override fun isLeakingObject(heapObject: HeapObject) = filter(heapObject)
}
}
}
}
16.LeakTrace 代表一个泄漏,计算泄漏字节,对象个数,聚合一样的泄漏,转为字符串
/**
* The best strong reference path from a GC root to the leaking object. "Best" here means the
* shortest prioritized path. A large number of distinct paths can generally be found leading
* to a leaking object. Shark prioritizes paths that don't go through known
* [LibraryLeakReferenceMatcher] (because those are known to create leaks so it's more interesting
* to find other paths causing leaks), then it prioritize paths that don't go through java local
* gc roots (because those are harder to reason about). Taking those priorities into account,
* finding the shortest path means there are less [LeakTraceReference] that can be suspected to
* cause the leak.
*
* 从 GC 根到泄漏对象的最佳引用路径。 这里的“最佳”是指最短的优先路径。通常可以找到大量不同路径到泄漏对象。
* Shark 一.优先考虑不通过已知 [LibraryLeakReferenceMatcher 的路径](因为已知这些路径会造成泄漏,
* 所以找到导致泄漏的其他路径更有趣),二.它优先考虑不通过 java local gc 根的路径(因为那些是 更难推理)。
* 考虑到这些优先级,找到最短路径意味着可以怀疑导致泄漏的 [LeakTraceReference] 更少。
*/
data class LeakTrace(
/**
* The Garbage Collection root that references the [LeakTraceReference.originObject] in
* the first [LeakTraceReference] of [referencePath].
* [referencePath] 的第一个 [LeakTraceReference] 中的 [LeakTraceReference.originObject] 的垃圾收集根。
*/
val gcRootType: GcRootType,
val referencePath: List<LeakTraceReference>,
val leakingObject: LeakTraceObject
) : Serializable {
/**
* The minimum number of bytes which would be freed if the leak was fixed.
* Null if the retained heap size was not computed.
* 如果泄漏得到修复,将释放的最小字节数。 如果未计算保留的堆大小,则为 Null。
*/
val retainedHeapByteSize: Int?
get() {
val allObjects = listOf(leakingObject) + referencePath.map { it.originObject }
return allObjects.filter { it.leakingStatus == LEAKING }
.mapNotNull { it.retainedHeapByteSize }
// The minimum released is the max held by a leaking object.
//释放的最小值是泄漏对象持有的最大值。
.max()
}
/**
* The minimum number of objects which would be unreachable if the leak was fixed. Null if the
* retained heap size was not computed.
* 如果泄漏得到修复,则无法访问的最小对象数。 如果未计算保留的堆大小,则为 Null。
*/
val retainedObjectCount: Int?
get() {
val allObjects = listOf(leakingObject) + referencePath.map { it.originObject }
return allObjects.filter { it.leakingStatus == LEAKING }
.mapNotNull { it.retainedObjectCount }
// The minimum released is the max held by a leaking object.
//释放的最小值是泄漏对象持有的最大值。
.max()
}
/**
* A part of [referencePath] that contains the references suspected to cause the leak.
* Starts at the last non leaking object and ends before the first leaking object.
* [referencePath] 的一部分,包含怀疑导致泄漏的引用。从最后一个非泄漏对象NOT_LEAKING开始,
* 在第一个泄漏对象LEAKING之前结束。
*/
// 类型是Sequence<LeakTraceReference>
val suspectReferenceSubpath
get() = referencePath.asSequence()
.filterIndexed { index, _ ->
referencePathElementIsSuspect(index)
}
/**
* A SHA1 hash that represents this leak trace. This can be useful to group together similar
* leak traces.
* 表示此泄漏跟踪的 SHA1 哈希。 这对于将相似的泄漏跟踪组合在一起很有用。
*
* The signature is a hash of [suspectReferenceSubpath].
* 签名是 [suspectReferenceSubpath] 的散列。
*/
val signature: String
get() = suspectReferenceSubpath
.joinToString(separator = "") { element ->
element.originObject.className + element.referenceGenericName
}
.createSHA1Hash()
/**
* Returns true if the [referencePath] element at the provided [index] contains a reference
* that is suspected to cause the leak, ie if [index] is greater than or equal to the index
* of the [LeakTraceReference] of the last non leaking object and strictly lower than the index
* of the [LeakTraceReference] of the first leaking object.
*
* 如果提供的 [index] 处的 [referencePath] 元素包含怀疑导致泄漏的引用,即如果 [index] 大于或等于
* 最后一个非泄漏对象的 [LeakTraceReference] 的索引,则返回 true 并且 严格低于第一个泄漏对象的
* [LeakTraceReference] 的索引。
*/
fun referencePathElementIsSuspect(index: Int): Boolean {
return when (referencePath[index].originObject.leakingStatus) {
UNKNOWN -> true
NOT_LEAKING -> index == referencePath.lastIndex ||
referencePath[index + 1].originObject.leakingStatus != NOT_LEAKING
else -> false
}
}
override fun toString(): String = leakTraceAsString(showLeakingStatus = true)
fun toSimplePathString(): String = leakTraceAsString(showLeakingStatus = false)
private fun leakTraceAsString(showLeakingStatus: Boolean): String {
var result = """
┬───
│ GC Root: ${gcRootType.description}
│
""".trimIndent()
//https://www.kancloud.cn/alex_wsc/android_kotlin/1049743
//trimIndent() 函数,则是把字符串行的左边空白对齐切割:
referencePath.forEachIndexed { index, element ->
val originObject = element.originObject
result += "\n"
result += originObject.toString(
firstLinePrefix = "├─ ",
additionalLinesPrefix = "│ ",
showLeakingStatus = showLeakingStatus,
/**
* When the GC Root is a Java Frame, Shark inserts the corresponding thread as an extra
* element in the leaktrace.
*/
typeName = if (index == 0 && gcRootType == JAVA_FRAME) {
"thread"
} else {
originObject.typeName
}
)
result += getNextElementString(this, element, index, showLeakingStatus)
}
result += "\n"
result += leakingObject.toString(
firstLinePrefix = "╰→ ",
additionalLinesPrefix = "$ZERO_WIDTH_SPACE ",
showLeakingStatus = showLeakingStatus
)
return result
}
enum class GcRootType(val description: String) {
JNI_GLOBAL("Global variable in native code"),
JNI_LOCAL("Local variable in native code"),
JAVA_FRAME("Java local variable"),
NATIVE_STACK("Input or output parameters in native code"),
STICKY_CLASS("System class"),
THREAD_BLOCK("Thread block"),
MONITOR_USED(
"Monitor (anything that called the wait() or notify() methods, or that is synchronized.)"
),
THREAD_OBJECT("Thread object"),
JNI_MONITOR("Root JNI monitor"),
;
companion object {
fun fromGcRoot(gcRoot: GcRoot): GcRootType = when (gcRoot) {
is GcRoot.JniGlobal -> JNI_GLOBAL
is GcRoot.JniLocal -> JNI_LOCAL
is GcRoot.JavaFrame -> JAVA_FRAME
is GcRoot.NativeStack -> NATIVE_STACK
is GcRoot.StickyClass -> STICKY_CLASS
is GcRoot.ThreadBlock -> THREAD_BLOCK
is GcRoot.MonitorUsed -> MONITOR_USED
is GcRoot.ThreadObject -> THREAD_OBJECT
is GcRoot.JniMonitor -> JNI_MONITOR
else -> throw IllegalStateException("Unexpected gc root $gcRoot")
}
}
}
companion object {
private fun getNextElementString(//在referencePathElementIsSuspect下面画上曲线~~~~~
leakTrace: LeakTrace,
reference: LeakTraceReference,
index: Int,
showLeakingStatus: Boolean
): String {
val static = if (reference.referenceType == STATIC_FIELD) " static" else ""
val referenceLine =
" ↓$static ${reference.owningClassSimpleName}.${reference.referenceDisplayName}"
return if (showLeakingStatus && leakTrace.referencePathElementIsSuspect(index)) {
val lengthBeforeReferenceName = referenceLine.lastIndexOf('.') + 1
val referenceLength = referenceLine.length - lengthBeforeReferenceName
val spaces = " ".repeat(lengthBeforeReferenceName)
val underline = "~".repeat(referenceLength)
"\n│$referenceLine\n│$spaces$underline"
} else {
"\n│$referenceLine"
}
}
internal const val ZERO_WIDTH_SPACE = '\u200b'
private const val serialVersionUID = -6315725584154386429
}
}
17.HeapAnalysis 分析结果,HeapAnalysisFailure或者HeapAnalysisSuccess,HeapAnalysisSuccess包括一个或者多个LibraryLeak或者ApplicationLeak
/**
* The result of an analysis performed by [HeapAnalyzer], either a [HeapAnalysisSuccess] or a
* [HeapAnalysisFailure]. This class is serializable however there are no guarantees of forward
* compatibility.
*
* [HeapAnalyzer] 执行的分析结果,可以是 [HeapAnalysisSuccess] 或 [HeapAnalysisFailure]。
* 此类是可序列化的,但不保证向前兼容性。
*/
sealed class HeapAnalysis : Serializable {
/**
* The hprof file that was analyzed.分析的 hprof 文件。
*/
abstract val heapDumpFile: File
/**
* The [System.currentTimeMillis] when this [HeapAnalysis] instance was created.
* 创建此 [HeapAnalysis] 实例时的 [System.currentTimeMillis]。
*/
abstract val createdAtTimeMillis: Long
/**
* Total time spent dumping the heap.
* 转储堆所花费的总时间。
*/
abstract val dumpDurationMillis: Long
/**
* Total time spent analyzing the heap.
* 分析堆所花费的总时间。
*/
abstract val analysisDurationMillis: Long
companion object {
private const val serialVersionUID: Long = -8657286725869987172
const val DUMP_DURATION_UNKNOWN: Long = -1
}
}
/**
* The analysis performed by [HeapAnalyzer] did not complete successfully.
* [HeapAnalyzer] 执行的分析未成功完成。
*/
data class HeapAnalysisFailure(
override val heapDumpFile: File,
override val createdAtTimeMillis: Long,
override val dumpDurationMillis: Long = DUMP_DURATION_UNKNOWN,
override val analysisDurationMillis: Long,
/**
* An exception wrapping the actual exception that was thrown.
* 包装抛出的实际异常的异常。
*/
val exception: HeapAnalysisException
) : HeapAnalysis() {
override fun toString(): String {
return """====================================
HEAP ANALYSIS FAILED
You can report this failure at https://github.com/square/leakcanary/issues
Please provide the stacktrace, metadata and the heap dump file.
====================================
STACKTRACE
$exception====================================
METADATA
Build.VERSION.SDK_INT: ${androidSdkInt()}
Build.MANUFACTURER: ${androidManufacturer()}
LeakCanary version: ${leakCanaryVersion()}
Analysis duration: $analysisDurationMillis ms
Heap dump file path: ${heapDumpFile.absolutePath}
Heap dump timestamp: $createdAtTimeMillis
===================================="""
}
companion object {
private const val serialVersionUID: Long = 8483254400637792414
}
}
/**
* The result of a successful heap analysis performed by [HeapAnalyzer].
* [HeapAnalyzer] 执行的成功堆分析的结果。
*/
data class HeapAnalysisSuccess(
override val heapDumpFile: File,
override val createdAtTimeMillis: Long,
override val dumpDurationMillis: Long = DUMP_DURATION_UNKNOWN,
override val analysisDurationMillis: Long,
val metadata: Map<String, String>,
/**
* The list of [ApplicationLeak] found in the heap dump by [HeapAnalyzer].
* [HeapAnalyzer] 在堆转储中找到的 [ApplicationLeak] 列表。
*/
val applicationLeaks: List<ApplicationLeak>,
/**
* The list of [LibraryLeak] found in the heap dump by [HeapAnalyzer].
* [HeapAnalyzer] 在堆转储中找到的 [LibraryLeak] 列表。
*/
val libraryLeaks: List<LibraryLeak>,
val unreachableObjects: List<LeakTraceObject>
) : HeapAnalysis() {
/**
* The list of [Leak] found in the heap dump by [HeapAnalyzer], ie all [applicationLeaks] and
* all [libraryLeaks] in one list.
* [HeapAnalyzer] 在堆转储中找到的 [Leak] 列表,即一个列表中的所有 [applicationLeaks] 和所有 [libraryLeaks]。
*/
val allLeaks: Sequence<Leak>
get() = applicationLeaks.asSequence() + libraryLeaks.asSequence()
override fun toString(): String {
return """====================================
HEAP ANALYSIS RESULT
====================================
${applicationLeaks.size} APPLICATION LEAKS
References underlined with "~~~" are likely causes.
Learn more at https://squ.re/leaks.
${
if (applicationLeaks.isNotEmpty()) "\n" + applicationLeaks.joinToString(
"\n\n"
) + "\n" else ""
}====================================
${libraryLeaks.size} LIBRARY LEAKS
A Library Leak is a leak caused by a known bug in 3rd party code that you do not have control over.
See https://square.github.io/leakcanary/fundamentals-how-leakcanary-works/#4-categorizing-leaks
${
if (libraryLeaks.isNotEmpty()) "\n" + libraryLeaks.joinToString(
"\n\n"
) + "\n" else ""
}====================================
${unreachableObjects.size} UNREACHABLE OBJECTS
An unreachable object is still in memory but LeakCanary could not find a strong reference path
from GC roots.
${
if (unreachableObjects.isNotEmpty()) "\n" + unreachableObjects.joinToString(
"\n\n"
) + "\n" else ""
}====================================
METADATA
Please include this in bug reports and Stack Overflow questions.
${
if (metadata.isNotEmpty()) "\n" + metadata.map { "${it.key}: ${it.value}" }.joinToString(
"\n"
) else ""
}
Analysis duration: $analysisDurationMillis ms
Heap dump file path: ${heapDumpFile.absolutePath}
Heap dump timestamp: $createdAtTimeMillis
Heap dump duration: ${if (dumpDurationMillis != DUMP_DURATION_UNKNOWN) "$dumpDurationMillis ms" else "Unknown"}
===================================="""
}
companion object {
private const val serialVersionUID: Long = 130453013437459642
}
}
/**
* A leak found by [HeapAnalyzer], either an [ApplicationLeak] or a [LibraryLeak].
* [HeapAnalyzer] 发现的泄漏,[ApplicationLeak] 或 [LibraryLeak]。
*/
sealed class Leak : Serializable {
/**
* Group of leak traces which share the same leak signature.
* 共享相同泄漏签名的一组泄漏跟踪。
*/
abstract val leakTraces: List<LeakTrace>
/**
* Sum of [LeakTrace.retainedHeapByteSize] for all elements in [leakTraces].
* Null if the retained heap size was not computed.
*
* [LeakTrace.retainedHeapByteSize] 中所有元素的总和 [leakTraces].
* Null 如果未计算保留的堆大小。
*/
val totalRetainedHeapByteSize: Int?
get() = if (leakTraces.first().retainedHeapByteSize == null) {
null
} else {
leakTraces.sumBy { it.retainedHeapByteSize!! }
}
/**
* Sum of [LeakTrace.retainedObjectCount] for all elements in [leakTraces].
* Null if the retained heap size was not computed.
* [leakTraces] 中所有元素的 [LeakTrace.retainedObjectCount] 总和。
* 如果未计算保留的堆大小,则为 Null。
*/
val totalRetainedObjectCount: Int?
get() = if (leakTraces.first().retainedObjectCount == null) {
null
} else {
leakTraces.sumBy { it.retainedObjectCount!! }
}
/**
* A unique SHA1 hash that represents this group of leak traces.
*
* For [ApplicationLeak] this is based on [LeakTrace.signature] and for [LibraryLeak] this is
* based on [LibraryLeak.pattern].
*
* 代表这组泄漏跟踪的唯一 SHA1 哈希。
* 对于 [ApplicationLeak],这是基于 [LeakTrace.signature],
* 对于 [LibraryLeak],这是基于 [LibraryLeak.pattern]。
*/
abstract val signature: String
abstract val shortDescription: String
override fun toString(): String {
return (if (totalRetainedHeapByteSize != null) "$totalRetainedHeapByteSize bytes retained by leaking objects\n" else "") +
(if (leakTraces.size > 1) "Displaying only 1 leak trace out of ${leakTraces.size} with the same signature\n" else "") +
"Signature: $signature\n" +
leakTraces.first()
}
companion object {
private const val serialVersionUID: Long = -2287572510360910916
}
}
/**
* A leak found by [HeapAnalyzer], where the only path to the leaking object required going
* through a reference matched by [pattern], as provided to a [LibraryLeakReferenceMatcher]
* instance. This is a known leak in library code that is beyond your control.
* [HeapAnalyzer] 发现的泄漏,其中泄漏对象的唯一路径需要通过 [pattern] 匹配的引用,
* 如提供给 [LibraryLeakReferenceMatcher] 实例。 这是您无法控制的库代码中的已知泄漏。
*/
data class LibraryLeak(
override val leakTraces: List<LeakTrace>,
/**
* The pattern that matched one of the references in each of [leakTraces], as provided to a
* [LibraryLeakReferenceMatcher] instance.
* 匹配每个 [leakTraces] 中的一个引用的模式,提供给 [LibraryLeakReferenceMatcher] 实例。
*/
val pattern: ReferencePattern,
/**
* A description that conveys what we know about this library leak.
* 传达我们对这个库泄漏的了解的描述。
*/
val description: String
) : Leak() {
override val signature: String
get() = pattern.toString().createSHA1Hash()
override val shortDescription: String
get() = pattern.toString()
override fun toString(): String {
return """Leak pattern: $pattern
Description: $description
${super.toString()}
"""
}
companion object {
private const val serialVersionUID: Long = 3943636164568681903
}
}
/**
* A leak found by [HeapAnalyzer] in your application.
* [HeapAnalyzer] 在您的应用程序中发现的泄漏。
*/
data class ApplicationLeak(
override val leakTraces: List<LeakTrace>
) : Leak() {
override val signature: String
get() = leakTraces.first().signature
override val shortDescription: String
get() {
val leakTrace = leakTraces.first()
//suspectReferenceSubpath第一个 或者 leakTrace.leakingObject.className
return leakTrace.suspectReferenceSubpath.firstOrNull()?.let { firstSuspectReferencePath ->
val referenceName = firstSuspectReferencePath.referenceGenericName
firstSuspectReferencePath.originObject.classSimpleName + "." + referenceName
} ?: leakTrace.leakingObject.className
}
// Override required to avoid the default toString() from data classes
override fun toString(): String {
return super.toString()
}
companion object {
private const val serialVersionUID: Long = 524928276700576863
}
}
private fun androidSdkInt(): Int {
return try {
val versionClass = Class.forName("android.os.Build\$VERSION")
val sdkIntField = versionClass.getDeclaredField("SDK_INT")
sdkIntField.get(null) as Int
} catch (e: Exception) {
-1
}
}
private fun androidManufacturer(): String {
return try {
val buildClass = Class.forName("android.os.Build")
val manufacturerField = buildClass.getDeclaredField("MANUFACTURER")
manufacturerField.get(null) as String
} catch (e: Exception) {
"Unknown"
}
}
private fun leakCanaryVersion(): String {
return try {
val versionHolderClass = Class.forName("leakcanary.internal.InternalLeakCanary")
val versionField = versionHolderClass.getDeclaredField("version")
versionField.isAccessible = true
versionField.get(null) as String
} catch (e: Exception) {
"Unknown"
}
}
18.Strings.kt 字符串截取,SHA1Hash
//截取最后一个segmentingChar之后的字符串
internal fun String.lastSegment(segmentingChar: Char): String {
val separator = lastIndexOf(segmentingChar)
return if (separator == -1) this else this.substring(separator + 1)
}
//计算SHA1Hash
internal fun String.createSHA1Hash(): String = encodeUtf8().sha1().hex()
19.ReferencePathNode引用路径的节点,细分为4种LibraryLeakRootNode,NormalRootNode,LibraryLeakChildNode,NormalNode
internal sealed class ReferencePathNode {
abstract val objectId: Long
interface LibraryLeakNode {
val matcher: LibraryLeakReferenceMatcher
}
sealed class RootNode : ReferencePathNode() {
abstract val gcRoot: GcRoot
class LibraryLeakRootNode(
override val objectId: Long,
override val gcRoot: GcRoot,
override val matcher: LibraryLeakReferenceMatcher
) : RootNode(), LibraryLeakNode
class NormalRootNode(
override val objectId: Long,
override val gcRoot: GcRoot
) : RootNode()
}
sealed class ChildNode : ReferencePathNode() {
abstract val parent: ReferencePathNode
/**
* The reference from the parent to this node
* 从父节点到此节点的引用
*/
abstract val refFromParentType: LeakTraceReference.ReferenceType
abstract val refFromParentName: String
/**
* If this node is an instance, then this is the id of the class that actually
* declares the node.
* 如果此节点是一个实例,则这是实际声明该节点的类的 id。
*/
abstract val owningClassId: Long
class LibraryLeakChildNode(
override val objectId: Long,
override val parent: ReferencePathNode,
override val refFromParentType: LeakTraceReference.ReferenceType,
override val refFromParentName: String,
override val matcher: LibraryLeakReferenceMatcher,
override val owningClassId: Long = 0
) : ChildNode(), LibraryLeakNode
class NormalNode(
override val objectId: Long,
override val parent: ReferencePathNode,
override val refFromParentType: LeakTraceReference.ReferenceType,
override val refFromParentName: String,
override val owningClassId: Long = 0
) : ChildNode()
}
}
20.FieldIdReader 读取的是InstanceDumpRecord里的fieldValues里的field的id
/**
* Simplified version of [FieldValuesReader] class that can only read an ID or skip a certain
* amount of bytes.
* [FieldValuesReader] 类的简化版本,只能读取 ID 或跳过一定数量的字节。
* 读取的是field的id
*/
internal class FieldIdReader(
private val record: InstanceDumpRecord,
private val identifierByteSize: Int//4
) {
private var position = 0
//读取id
fun readId(): Long {
// As long as we don't interpret IDs, reading signed values here is fine.
val value = when (identifierByteSize) {
1 -> readByteId(position, record.fieldValues)
2 -> readShortId(position, record.fieldValues)
4 -> readIntId(position, record.fieldValues)
8 -> readLongId(position, record.fieldValues)
else -> error("ID Length must be 1, 2, 4, or 8")
}
position += identifierByteSize
return value
}
fun skipBytes(count: Int) {
position += count
}
private fun readByteId(
index: Int,
array: ByteArray
) =
array[index].toLong()
private fun readShortId(
index: Int,
array: ByteArray
): Long {
var pos = index
return (array[pos++] and 0xff shl 8
or (array[pos] and 0xff)).toLong()
}
private fun readIntId(
index: Int,
array: ByteArray
): Long {
var pos = index
return (array[pos++] and 0xff shl 24
or (array[pos++] and 0xff shl 16)
or (array[pos++] and 0xff shl 8)
or (array[pos] and 0xff)).toLong()
}
private fun readLongId(
index: Int,
array: ByteArray
): Long {
var pos = index
return (array[pos++] and 0xffL shl 56shl左移动
or (array[pos++] and 0xffL shl 48)
or (array[pos++] and 0xffL shl 40)
or (array[pos++] and 0xffL shl 32)
or (array[pos++] and 0xffL shl 24)
or (array[pos++] and 0xffL shl 16)
or (array[pos++] and 0xffL shl 8)
or (array[pos] and 0xffL))
}
@Suppress("NOTHING_TO_INLINE") // Syntactic sugar.
private inline infix fun Byte.and(other: Long): Long = toLong() and other
@Suppress("NOTHING_TO_INLINE") // Syntactic sugar.
private inline infix fun Byte.and(other: Int): Int = toInt() and other
}
21.KeyedWeakReferenceMirror从heap里读取的KeyedWeakReference类的信息
internal class KeyedWeakReferenceMirror(
val referent: ReferenceHolder,
val key: String,
// The name field does not exist in pre 1.0 heap dumps.
val description: String,
// null in pre 2.0 alpha 3 heap dumps
val watchDurationMillis: Long?,
// null in pre 2.0 alpha 3 heap dumps, -1 if the instance is not retained.
val retainedDurationMillis: Long?
) {
val hasReferent = referent.value != ValueHolder.NULL_REFERENCE
val isRetained = retainedDurationMillis == null || retainedDurationMillis != -1L
companion object {
private const val UNKNOWN_LEGACY = "Unknown (legacy)"
fun fromInstance(
weakRef: HeapInstance,
// Null for pre 2.0 alpha 3 heap dumps
heapDumpUptimeMillis: Long?dump开始的时间
): KeyedWeakReferenceMirror {
val keyWeakRefClassName = weakRef.instanceClassName
val watchDurationMillis = if (heapDumpUptimeMillis != null) {
//这里[]调用的HeapObject的get方法,最终调用readField
//watchUptimeMillis开始进行观察的时间
heapDumpUptimeMillis - weakRef[keyWeakRefClassName, "watchUptimeMillis"]!!.value.asLong!!
} else {
null
}
val retainedDurationMillis = if (heapDumpUptimeMillis != null) {
//泄漏开始的时间,在watchUptimeMillis 5s之后
val retainedUptimeMillis =
weakRef[keyWeakRefClassName, "retainedUptimeMillis"]!!.value.asLong!!
if (retainedUptimeMillis == -1L) -1L else heapDumpUptimeMillis - retainedUptimeMillis
} else {
null
}
val keyString = weakRef[keyWeakRefClassName, "key"]!!.value.readAsJavaString()!!
// Changed from name to description after 2.0
val description = (weakRef[keyWeakRefClassName, "description"]
?: weakRef[keyWeakRefClassName, "name"])?.value?.readAsJavaString()
?: UNKNOWN_LEGACY
return KeyedWeakReferenceMirror(
watchDurationMillis = watchDurationMillis,
retainedDurationMillis = retainedDurationMillis,
referent = weakRef["java.lang.ref.Reference", "referent"]!!.value.holder as ReferenceHolder,
key = keyString,
description = description
)
}
}
}
22.ObjectDominators 计算泄漏大小,数量的类,todo细节
/**
* Exposes high level APIs to compute and render a dominator tree. This class
* needs to be public to be used by other LeakCanary modules but is internal and
* its API might change at any moment.
*
* Note that the exposed APIs are not optimized for speed, memory or IO.
*
* Eventually this capability should become part of the Shark public APIs, please
* open an issue if you'd like to use this directly.
*
* 公开高级 API 以计算和呈现支配树。 此类需要公开以供其他 LeakCanary 模块使用,但它是内部的,
* 其 API 可能随时更改。 请注意,公开的 API 并未针对速度、内存或 IO 进行优化。
* 最终这个功能应该成为 Shark 公共 API 的一部分,如果你想直接使用它,请打开一个问题。
*
* Dominator
* 统治者
*/
class ObjectDominators {
internal data class DominatorNode(
val shallowSize: Int,//浅 todo
val retainedSize: Int,//泄漏的大小?? todo
val retainedCount: Int,//泄漏对象数量?? todo
val dominatedObjectIds: List<Long>//dominated占主导地位
)
}
23.ShallowSizeCalculator提供内存中对象的浅层大小的近似值。
/**
* Provides approximations for the shallow size of objects in memory.
*
* Determining the actual shallow size of an object in memory is hard, as it changes for each VM
* implementation, depending on the various memory layout optimizations and bit alignment.
*
* More on this topic: https://dev.to/pyricau/the-real-size-of-android-objects-1i2e
*
* 提供内存中对象的浅层大小的近似值。
* 确定内存中对象的实际浅层大小很困难,因为它会因每个 VM 实现而变化,具体取决于各种内存布局优化和位对齐。
*/
internal class ShallowSizeCalculator(private val graph: HeapGraph) {
fun computeShallowSize(objectId: Long): Int {
return when (val heapObject = graph.findObjectById(objectId)) {
is HeapInstance -> {
if (heapObject.instanceClassName == "java.lang.String") {
// In PathFinder we ignore the value field of String instances when building the dominator
// tree, so we add that size back here.
val valueObjectId =
heapObject["java.lang.String", "value"]?.value?.asNonNullObjectId
heapObject.byteSize + if (valueObjectId != null) {
computeShallowSize(valueObjectId)
} else {
0
}
} else {
// Total byte size of fields for instances of this class, as registered in the class dump.
// The actual memory layout likely differs.
heapObject.byteSize
}
}
// Number of elements * object id size
//元素数 * 对象 ID 大小
is HeapObjectArray -> {
if (heapObject.isSkippablePrimitiveWrapperArray) {
// In PathFinder we ignore references sfrom primitive wrapper arrayss when building the
// dominator tree, so we add that size back here.
//在 PathFinder 中,我们在构建支配树时忽略了原始包装器数组的引用,因此我们在此处添加了该大小。
val elementIds = heapObject.readRecord().elementIds
//数组
val shallowSize = elementIds.size * graph.identifierByteSize
val firstNonNullElement = elementIds.firstOrNull { it != ValueHolder.NULL_REFERENCE }
if (firstNonNullElement != null) {
//第一个元素
val sizeOfOneElement = computeShallowSize(firstNonNullElement)
//非空元素数量
val countOfNonNullElements = elementIds.count { it != ValueHolder.NULL_REFERENCE }
shallowSize + (sizeOfOneElement * countOfNonNullElements)
} else {
shallowSize
}
} else {
heapObject.readByteSize()
}
}
// Number of elements * primitive type size
//元素数 * 原始类型大小
is HeapPrimitiveArray -> heapObject.readByteSize()
// This is probably way off but is a cheap approximation.
//这可能还有一段路要走,但这是一个廉价的近似值。
is HeapClass -> heapObject.recordSize
}
}
}