LeakCanary 原理分析
Posted 清浅岁月
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeakCanary 原理分析相关的知识,希望对你有一定的参考价值。
LeakCanary 原理分析
LeakCanary的初始化
LeakCanary2.3的引入:
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.3'
2.3版本无需在Application中做额外操作。
深入了解一下具体是如何初始化的,我的是2.3版本的,2.0以下版本应该和这个不一样:
https://github.com/square/leakcanary/blob/main/leakcanary-object-watcher-android/src/main/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.squareup.leakcanary.objectwatcher"
>
<application>
<provider
android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
android:authorities="$applicationId.leakcanary-installer"
android:enabled="@bool/leak_canary_watcher_auto_install"
android:exported="false"/>
</application>
</manifest>
在该lib下的AppWatcherInstaller的代码如下:
package leakcanary.internal
import android.app.Application
import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.net.Uri
import leakcanary.AppWatcher
/**
* Content providers are loaded before the application class is created. [AppWatcherInstaller] is
* used to install [leakcanary.AppWatcher] on application start.
*/
internal sealed class AppWatcherInstaller : ContentProvider()
/**
* [MainProcess] automatically sets up the LeakCanary code that runs in the main app process.
*/
internal class MainProcess : AppWatcherInstaller()
/**
* When using the `leakcanary-android-process` artifact instead of `leakcanary-android`,
* [LeakCanaryProcess] automatically sets up the LeakCanary code
*/
internal class LeakCanaryProcess : AppWatcherInstaller()
override fun onCreate(): Boolean
super.onCreate()
AppWatcher.config = AppWatcher.config.copy(enabled = false)
return true
override fun onCreate(): Boolean
val application = context!!.applicationContext as Application
InternalAppWatcher.install(application) // 重点在这里
return true
override fun query(
uri: Uri,
strings: Array<String>?,
s: String?,
strings1: Array<String>?,
s1: String?
): Cursor?
return null
....
是通过ContentProvider来实现的,ContentProvider的初始化要比application的oncreate方法还要早一些。
InternalAppWatcher.install(application)中具体的内容:
fun install(application: Application)
SharkLog.logger = DefaultCanaryLog()
checkMainThread()
if (this::application.isInitialized)
return
InternalAppWatcher.application = application
val configProvider = AppWatcher.config
ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
onAppWatcherInstalled(application)
做了activity与fragment的检测的初始化,和onAppWatcherInstalled。
先看一下activity的监听。
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package leakcanary.internal
import android.app.Activity
import android.app.Application
import leakcanary.AppWatcher.Config
import leakcanary.ObjectWatcher
import leakcanary.internal.InternalAppWatcher.noOpDelegate
internal class ActivityDestroyWatcher private constructor(
private val objectWatcher: ObjectWatcher,
private val configProvider: () -> Config
)
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate()
override fun onActivityDestroyed(activity: Activity)
if (configProvider().watchActivities)
objectWatcher.watch(
activity, "$activity::class.java.name received Activity#onDestroy() callback"
)
companion object
fun install(
application: Application,
objectWatcher: ObjectWatcher,
configProvider: () -> Config
)
val activityDestroyWatcher =
ActivityDestroyWatcher(objectWatcher, configProvider)
application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
根据lifecycle,根据application注册registerActivityLifecycleCallbacks接口,使用objectWatcher.watch进行检测。
再看看Fragment的检测,fragment的检测比较麻烦。
/*
* Copyright (C) 2019 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package leakcanary.internal
import android.app.Activity
import android.app.Application
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.O
import android.os.Bundle
import leakcanary.AppWatcher
import leakcanary.ObjectWatcher
import leakcanary.internal.InternalAppWatcher.noOpDelegate
/**
* Internal class used to watch for fragments leaks.
*/
internal object FragmentDestroyWatcher
private const val ANDROIDX_FRAGMENT_CLASS_NAME = "androidx.fragment.app.Fragment"
private const val ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME =
"leakcanary.internal.AndroidXFragmentDestroyWatcher"
// Using a string builder to prevent Jetifier from changing this string to Android X Fragment
@Suppress("VariableNaming", "PropertyName")
private val ANDROID_SUPPORT_FRAGMENT_CLASS_NAME =
StringBuilder("android.").append("support.v4.app.Fragment")
.toString()
private const val ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME =
"leakcanary.internal.AndroidSupportFragmentDestroyWatcher"
fun install(
application: Application,
objectWatcher: ObjectWatcher,
configProvider: () -> AppWatcher.Config
)
val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()
if (SDK_INT >= O)
fragmentDestroyWatchers.add(
AndroidOFragmentDestroyWatcher(objectWatcher, configProvider)
)
getWatcherIfAvailable(
ANDROIDX_FRAGMENT_CLASS_NAME,
ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
objectWatcher,
configProvider
)?.let
fragmentDestroyWatchers.add(it)
getWatcherIfAvailable(
ANDROID_SUPPORT_FRAGMENT_CLASS_NAME,
ANDROID_SUPPORT_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
objectWatcher,
configProvider
)?.let
fragmentDestroyWatchers.add(it)
if (fragmentDestroyWatchers.size == 0)
return
application.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks by noOpDelegate()
override fun onActivityCreated(
activity: Activity,
savedInstanceState: Bundle?
)
for (watcher in fragmentDestroyWatchers)
watcher(activity)
)
......
应该是根据activity获取一个fragmentDestroyWatcher的集合,给activity加载的每一个fragment创建一个监听器。
最后也是给每一个监听器注册LifecycleCallback将activity的生命周期回传到fragment的监听器中。
看一下fragment的具体的监听器度做了什么工作:
/*
* Copyright (C) 2018 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("DEPRECATION")
package leakcanary.internal
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Fragment
import android.app.FragmentManager
import leakcanary.AppWatcher.Config
import leakcanary.ObjectWatcher
@SuppressLint("NewApi")
internal class AndroidOFragmentDestroyWatcher(
private val objectWatcher: ObjectWatcher,
private val configProvider: () -> Config
) : (Activity) -> Unit
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks()
override fun onFragmentViewDestroyed(
fm: FragmentManager,
fragment: Fragment
)
val view = fragment.view
if (view != null && configProvider().watchFragmentViews)
objectWatcher.watch(
view, "$fragment::class.java.name received Fragment#onDestroyView() callback " +
"(references to its views should be cleared to prevent leaks)"
)
override fun onFragmentDestroyed(
fm: FragmentManager,
fragment: Fragment
)
if (configProvider().watchFragments)
objectWatcher.watch(
fragment, "$fragment::class.java.name received Fragment#onDestroy() callback"
)
override fun invoke(activity: Activity)
val fragmentManager = activity.fragmentManager
fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true)
注册fragment的LifecycleCallbacks回调,在fragmen 的onFragmentDestroyed和onFragmentViewDestroyed中使用objectWatcher.watch做了检测。
在看一下onAppWatcherInstalled做了什么:
private val onAppWatcherInstalled: (Application) -> Unit
init
val internalLeakCanary = try
val leakCanaryListener = Class.forName("leakcanary.internal.InternalLeakCanary")
leakCanaryListener.getDeclaredField("INSTANCE")
.get(null)
catch (ignored: Throwable)
NoLeakCanary
@kotlin.Suppress("UNCHECKED_CAST")
onAppWatcherInstalled = internalLeakCanary as (Application) -> Unit
应该是更具反射初始化internalLeakCanary,看一下internalLeakCanary做多了什么操作:
internal object InternalLeakCanary : (Application) -> Unit, OnObjectRetainedListener
override fun invoke(application: Application)
this.application = application
checkRunningInDebuggableBuild()
AppWatcher.objectWatcher.addOnObjectRetainedListener(this)
val heapDumper = AndroidHeapDumper(application, leakDirectoryProvider)
val gcTrigger = GcTrigger.Default
val configProvider = LeakCanary.config
val handlerThread = HandlerThread(LEAK_CANARY_THREAD_NAME)
handlerThread.start()
val backgroundHandler = Handler(handlerThread.looper)
heapDumpTrigger = HeapDumpTrigger(
application, backgroundHandler, AppWatcher.objectWatcher, gcTrigger, heapDumper,
configProvider
)
application.registerVisibilityListener applicationVisible ->
this.applicationVisible = applicationVisible
heapDumpTrigger.onApplicationVisibilityChanged(applicationVisible)
registerResumedActivityListener(application)
addDynamicShortcut(application)
disableDumpHeapInTests()
.....
override fun onObjectRetained()
if (this::heapDumpTrigger.isInitialized)
heapDumpTrigger.onObjectRetained()
.......
添加addOnObjectRetainedListener,heapDumper,gcTrigger,handlerThread等一些准备工作。初始化工作基本完成了。
下面具体看一下objectWatcher.watch中具体是怎么做检测的,objectWatcher中的代码有点多,想看直接可以在这里看
https://github.com/square/leakcanary/blob/main/leakcanary-object-watcher/src/main/java/leakcanary/ObjectWatcher.kt
*/
@Synchronized fun watch(
watchedObject: Any,
description: String
)
if (!isEnabled())
return
removeWeaklyReachableObjects()
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
val reference =
KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
SharkLog.d
"Watching " +
(if (watchedObject is Class<*>) watchedObject.toString() else "instance of $watchedObject.javaClass.name") +
(if (description.isNotEmpty()) " ($description)" else "") +
" with key $key"
watchedObjects[key] = reference
checkRetainedExecutor.execute
moveToRetained(key)
......
@Synchronized private fun moveToRetained(key: String)
removeWeaklyReachableObjects()
val retainedRef = watchedObjects[key]
if (retainedRef != null)
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
onObjectRetainedListeners.forEach it.onObjectRetained()
最后 moveToRetained执行onObjectRetainedListeners集合中的onObjectRetained()方法。onObjectRetained()方法既InternalLeakCanary重写的那个方法,这个方法中如何处理的:
InternalLeakCanary的onObjectRetained方法
override fun onObjectRetained()
if (this::heapDumpTrigger.isInitialized)
heapDumpTrigger.onObjectRetained()
HeapDumpTrigger的onObjectRetained和scheduleRetainedObjectCheck方法
fun onObjectRetained()
scheduleRetainedObjectCheck(
reason = "found new object retained",
rescheduling = false
)
private fun scheduleRetainedObjectCheck(
reason: String,
rescheduling: Boolean,
delayMillis: Long = 0L
)
val checkCurrentlyScheduledAt = checkScheduledAt
if (checkCurrentlyScheduledAt > 0)
val scheduledIn = checkCurrentlyScheduledAt - SystemClock.uptimeMillis()
SharkLog.d "Ignoring request to check for retained objects ($reason), already scheduled in $scheduledInms"
return
else
val verb = if (rescheduling) "Rescheduling" else "Scheduling"
val delay = if (delayMillis > 0) " in $delayMillisms" else ""
SharkLog.d "$verb check for retained objects$delay because $reason"
checkScheduledAt = SystemClock.uptimeMillis() + delayMillis
backgroundHandler.postDelayed(
checkScheduledAt = 0
checkRetainedObjects(reason)
, delayMillis)
核心在checkRetainedObjects方法中:
private fun checkRetainedObjects(reason: String)
val config = configProvider()
// A tick will be rescheduled when this is turned back on.
if (!config.dumpHeap)
SharkLog.d "Ignoring check for retained objects scheduled because $reason: LeakCanary.Config.dumpHeap is false"
return
var retainedReferenceCount = objectWatcher.retainedObjectCount
if (retainedReferenceCount > 0)
gcTrigger.runGc()
retainedReferenceCount = objectWatcher.retainedObjectCount
if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
if (!config.dumpHeapWhenDebugging && DebuggerControl.isDebuggerAttached)
onRetainInstanceListener.onEvent(DebuggerIsAttached)
showRetainedCountNotification(
objectCount = retainedReferenceCount,
contentText = application.getString(
R.string.leak_canary_notification_retained_debugger_attached
)
)
scheduleRetainedObjectCheck(
reason = "debugger is attached",
rescheduling = true,
delayMillis = WAIT_FOR_DEBUG_MILLIS
)
return
val now = SystemClock.uptimeMillis()
val elapsedSinceLastDumpMillis = now - lastHeapDumpUptimeMillis
if (elapsedSinceLastDumpMillis < WAIT_BETWEEN_HEAP_DUMPS_MILLIS)
onRetainInstanceListener.onEvent(DumpHappenedRecently)
showRetainedCountNotification(
objectCount = retainedReferenceCount,
contentText = application.getString(R.string.leak_canary_notification_retained_dump_wait)
)
scheduleRetainedObjectCheck(
reason = "previous heap dump was $elapsedSinceLastDumpMillisms ago (< $WAIT_BETWEEN_HEAP_DUMPS_MILLISms)",
rescheduling = true,
delayMillis = WAIT_BETWEEN_HEAP_DUMPS_MILLIS - elapsedSinceLastDumpMillis
)
return
SharkLog.d "Check for retained objects found $retainedReferenceCount objects, dumping the heap"
dismissRetainedCountNotification()
dumpHeap(retainedReferenceCount, retry = true)
最后生成dumpHeap信息
private fun dumpHeap(
retainedReferenceCount: Int,
retry: Boolean
)
saveResourceIdNamesToMemory()
val heapDumpUptimeMillis = SystemClock.uptimeMillis()
KeyedWeakReference.heapDumpUptimeMillis = heapDumpUptimeMillis
val heapDumpFile = heapDumper.dumpHeap()
if (heapDumpFile == null)
if (retry)
SharkLog.d "Failed to dump heap, will retry in $WAIT_AFTER_DUMP_FAILED_MILLIS ms"
scheduleRetainedObjectCheck(
reason = "failed to dump heap",
rescheduling = true,
delayMillis = WAIT_AFTER_DUMP_FAILED_MILLIS
)
else
SharkLog.d "Failed to dump heap, will not automatically retry"
showRetainedCountNotification(
objectCount = retainedReferenceCount,
contentText = application.getString(
R.string.leak_canary_notification_retained_dump_failed
)
)
return
lastDisplayedRetainedObjectCount = 0
lastHeapDumpUptimeMillis = SystemClock.uptimeMillis()
objectWatcher.clearObjectsWatchedBefore(heapDumpUptimeMillis)
HeapAnalyzerService.runAnalysis(application, heapDumpFile)
之后是对生成的文件进行分析HeapAnalyzerService.runAnalysis(application, heapDumpFile):
HeapAnalyzerService是一个前台service,启动service开始分析文件。
fun runAnalysis(
context: Context,
heapDumpFile: File
)
val intent = Intent(context, HeapAnalyzerService::class.java)
intent.putExtra(HEAPDUMP_FILE_EXTRA, heapDumpFile)
startForegroundService(context, intent)
启动service后进行处理。
override fun onHandleIntentInForeground(intent: Intent?)
if (intent == null || !intent.hasExtra(HEAPDUMP_FILE_EXTRA))
SharkLog.d "HeapAnalyzerService received a null or empty intent, ignoring."
return
// Since we're running in the main process we should be careful not to impact it.
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)
val heapDumpFile = intent.getSerializableExtra(HEAPDUMP_FILE_EXTRA) as File
val config = LeakCanary.config
val heapAnalysis = if (heapDumpFile.exists())
analyzeHeap(heapDumpFile, config)
else
missingFileFailure(heapDumpFile)
onAnalysisProgress(REPORTING_HEAP_ANALYSIS)
config.onHeapAnalyzedListener.onHeapAnalyzed(heapAnalysis)
private fun analyzeHeap(
heapDumpFile: File,
config: Config
): HeapAnalysis
val heapAnalyzer = HeapAnalyzer(this)
val proguardMappingReader = try
ProguardMappingReader(assets.open(PROGUARD_MAPPING_FILE_NAME))
catch (e: IOException)
null
return heapAnalyzer.analyze(
heapDumpFile = heapDumpFile,
leakingObjectFinder = config.leakingObjectFinder,
referenceMatchers = config.referenceMatchers,
computeRetainedHeapSize = config.computeRetainedHeapSize,
objectInspectors = config.objectInspectors,
metadataExtractor = config.metadataExtractor,
proguardMapping = proguardMappingReader?.readProguardMapping()
)
首先判断,文件是否存在,不存在按miss处理,存在进行分析,具体的分析。
dump对象及分析
dump对象
hprof是JDK提供的一种JVM TI Agent native工具。JVM TI,全拼是JVM Tool interface,是JVM提供的一套标准的C/C++编程接口,是实现Debugger、Profiler、Monitor、Thread Analyser等工具的统一基础,在主流Java虚拟机中都有实现。hprof工具事实上也是实现了这套接口,可以认为是一套简单的profiler agent工具。我们在新知周推:10.8-10.14(启动篇)中也提到过,可以参考其中美团的文章。
用过Android Studio Profiler工具的同学对hprof文件都不会陌生,当我们使用Memory Profiler工具的Dump Java heap图标的时候,profiler工具就会去捕获你的内存分配情况。但是捕获以后,只有在Memory Profiler正在运行的时候我们才能查看,那么我们要怎么样去保存当时的内存使用情况呢,又或者我想用别的工具来分析堆分配情况呢,这时候hprof文件就派上用场了。Android Studio可以把这些对象给export到hprof文件中去。
LeakCanary也是使用的hprof文件进行对象存储。hprof文件比较简单,整体按照 前置信息 + 记录表的格式来组织的。但是记录的种类相当之多。具体种类可以查看HPROF Agent。
同时,android中也提供了一个简便的方法Debug.dumphprofData(filePath)可以把对象dump到指定路径下的hprof文件中。LeakCanary使用使用Shark库来解析Hprof文件中的各种record,比较高效,使用Shark中的HprofReader和HprofWriter来进行读写解析,获取我们需要的信息。大家可以关注一些比较重要的,比如:
Class Dump
Instance Dump
Object Array Dump
Primitive Array Dump
dump具体的代码在AndroidHeapDumper类中。HprofReader和HprofWriter过于复杂,有兴趣的直接查看源码吧。
return try
listener.onAnalysisProgress(PARSING_HEAP_DUMP)
Hprof.open(heapDumpFile)
.use hprof ->
val graph = HprofHeapGraph.indexHprof(hprof, proguardMapping)
val helpers =
FindLeakInput(graph, referenceMatchers, computeRetainedHeapSize, objectInspectors)
helpers.analyzeGraph(
metadataExtractor, leakingObjectFinder, heapDumpFile, analysisStartNanoTime
)
catch (exception: Throwable)
HeapAnalysisFailure(
heapDumpFile, System.currentTimeMillis(), since(analysisStartNanoTime),
HeapAnalysisException(exception)
)
其中的核心方法
private fun FindLeakInput.analyzeGraph(
metadataExtractor: MetadataExtractor,
leakingObjectFinder: LeakingObjectFinder,
heapDumpFile: File,
analysisStartNanoTime: Long
): HeapAnalysisSuccess
listener.onAnalysisProgress(EXTRACTING_METADATA)
val metadata = metadataExtractor.extractMetadata(graph)
listener.onAnalysisProgress(FINDING_RETAINED_OBJECTS)
val leakingObjectIds = leakingObjectFinder.findLeakingObjectIds(graph)
val (applicationLeaks, libraryLeaks) = findLeaks(leakingObjectIds)
return HeapAnalysisSuccess(
heapDumpFile = heapDumpFile,
createdAtTimeMillis = System.currentTimeMillis(),
analysisDurationMillis = since(analysisStartNanoTime),
metadata = metadata,
applicationLeaks = applicationLeaks,
libraryLeaks = libraryLeaks
)
首先调用HprofHeapGraph.indexHprof方法,这个方法会把dump出来的各种实例instance,Class类对象和Array对象等都建立起查询的索引,以record的id作为key,把需要的信息都存储在Map中便于后续取用
调用findLeakInput.findLeak方法,这个方法会从GC Root开始查询,找到最短的一条导致泄露的引用链,然后再根据这条引用链构建出LeakTrace。
把查询出来的LeakTrace对外展示
最后返回分析成功的HeapAnalysisSuccess对象将数据在log中打印,或者通知栏通知。
总结:
- LeakCanary的初始化,使用ContentProvider
- 添加fragment与activity的监听,在onDestory的触发ObjectWatcher的检测
- ObjectWatcher使用HeapDumpTrigger,产生dumpHprof文件
- 使用HeapAnalyzerService对dumpHprof进行分析
- HeapAnalyzerService使用sharklib调用jvm的TI
- sharklib使用HprofHeapGraph.indexHprof建立起索引,以record的id作为key存储map,findLeakInput.findLeak从gc节点查询找到最短引用链构建出LeakTrace
- 最后生成一个AnalyzerSucess对象将上述信息封装,分发出来。
参考文档:https://www.jianshu.com/p/e36d60ab7ad2
以上是关于LeakCanary 原理分析的主要内容,如果未能解决你的问题,请参考以下文章