leakcanary shark 库解析 三

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
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容