LeakCanary是Android上用于检查内存泄漏的工具,LeakCanary大大减少因内存泄漏导致的内存溢出(OutOfMemoryError)崩溃。
从1.6.3开始,LeakCanary就使用Kotlin重写了一次,这里的的源码来自于版本2.5,加入引用:
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.5'
LeakCanary包结构

leakcanary-android
集成入口模块,提供 LeakCanary 安装,公开 API 等能力
leakcanary-android-process
和 leakcanary-android 一样,区别是会在单独的进程进行分析
leakcanary-android-core
核心模块
leakcanary-object-watcher-android,leakcanary-object-watcher-android-androidx,leakcanary-watcher-android-support-fragments
对象实例观察模块,在 Activity,Fragment 等对象的生命周期中,注册对指定对象实例的观察,有 Activity,Fragment,Fragment View,ViewModel 等
shark-android
提供特定于 Android 平台的分析能力。例如设备的信息,Android 版本,已知的内存泄露问题等
shark
hprof 文件解析与分析的入口模块
shark-graph
分析堆中对象的关系图模块
shark-hprof
解析 hprof 文件模块
shark-log
日志模块
初始化
LeakCanary 2.0不需要添加代码便可以跟随APP启动,省去了1.6版本前需要install的代码。原理在于利用了ContentProvider的特性,ContentProvider.onCreate方法会先于Application.onCreate执行。
leakcanary库中声明的ContentProvider。
//注册ContentProvider @leakcanary-object-watcher-android/src/main/AndroidManifest.xml
<application>
<provider
android:name="leakcanary.internal.AppWatcherInstaller$MainProcess"
android:authorities="${applicationId}.leakcanary-installer"
android:exported="false"/>
</application>
//@AppWatcherInstaller.kt
internal class LeakCanaryProcess : AppWatcherInstaller() {
override fun onCreate(): Boolean {
super.onCreate()
AppWatcher.config = AppWatcher.config.copy(enabled = false)
return true
}
}
override fun onCreate(): Boolean {
//获取application
val application = context!!.applicationContext as Application
//-->2.1 加载LeakCanary
InternalAppWatcher.install(application)
return true
}
}
//2.1 加载LeakCanary @InternalAppWatcher.kt
fun install(application: Application) {
...
//检查当前线程是否有主线程
checkMainThread()
if (this::application.isInitialized) {
//如果LeakCanary已经加载过,直接放回
return
}
InternalAppWatcher.application = application
val configProvider = { AppWatcher.config }
//-->2.1监视Activity
ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
//-->2.2监视Fragment
FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
//-->2.3调用上层模块InternalLeakCanary.invoke
onAppWatcherInstalled(application)
}
监听器利用了Activity、fragment的生命周期回调,在ActivityDestroyWatcher类中,获取该销毁的activity,添加了该activity的监听。
companion object {
fun install(
application: Application,
objectWatcher: ObjectWatcher,
configProvider: () -> Config
) {
//-->2.1.1 创建Activity destroy监听回调
val activityDestroyWatcher =
ActivityDestroyWatcher(objectWatcher, configProvider)
//-->2.1.2 同Application绑定
application.registerActivityLifecycleCallbacks(activityDestroyWatcher.lifecycleCallbacks)
}
}
//2.1.1 创建Activity destroy监听回调
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
//Activity destroy触发存在对象检查
if (configProvider().watchActivities) {
// -->2.1.2 objectWatcher监视activity
objectWatcher.watch(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
}
lifecycleCallbacks监听Activity的onDestroy方法,正常情况下activity在onDestroy后需要立即被回收,onActivityDestroyed方法最终会调用RefWatcher.watch方法:
@Synchronized fun watch(
watchedObject: Any,
description: String
) {
if (!isEnabled()) {
return
}
removeWeaklyReachableObjects()
val key = UUID.randomUUID()
.toString()
val watchUptimeMillis = clock.uptimeMillis()
//根据activity创建对应的弱引用,并绑定ReferenceQueue
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"
}
//将reference保存到watchedObjects数组中
watchedObjects[key] = reference
//启动延时5s任务
checkRetainedExecutor.execute {
//获取GC无法回收的Activity
moveToRetained(key)
}
}
监测机制利用了Java的WeakReference和ReferenceQueue,通过将Activity包装到WeakReference中,被WeakReference包装过的Activity对象如果被回收,该WeakReference引用会被放到ReferenceQueue中,通过监测ReferenceQueue里面的内容就能检查到Activity是否能够被回收。
ReferenceQueue:
引用队列,换言之就是存放引用的队列,保存的是Reference对象。其作用在于Reference对象所引用的对象被GC回收时,该Reference对象将会被加入引用队列的队尾。
//获取GC无法回收的Activity
@Synchronized private fun moveToRetained(key: String) {
removeWeaklyReachableObjects()
val retainedRef = watchedObjects[key]
if (retainedRef != null) {
//保存当前时间作为泄漏时间
retainedRef.retainedUptimeMillis = clock.uptimeMillis()
//通知InternalLeakCanary发生内存泄漏
onObjectRetainedListeners.forEach { it.onObjectRetained() }
}
}
private fun removeWeaklyReachableObjects() {
// WeakReferences are enqueued as soon as the object to which they point to becomes weakly
// reachable. This is before finalization or garbage collection has actually happened.
var ref: KeyedWeakReference?
do {
ref = queue.poll() as KeyedWeakReference?
if (ref != null) {
//在watchedObjects中删除不发送内存泄漏对象,剩下内存泄漏对象
watchedObjects.remove(ref.key)
}
} while (ref != null)
}
//通知InternalLeakCanary发生内存泄漏 @HeapDumpTrigger.kt
override fun onObjectRetained() {
if (this::heapDumpTrigger.isInitialized) {
//通知heapDumpTrigger有内存泄漏
heapDumpTrigger.onObjectRetained()
}
}
-
LeakCanary检测内存泄漏的基本流程

1、 首先通过removeWeaklyReachablereference来移除已经被回收的Activity引用
2、 通过gone(reference)判断当前弱引用对应的Activity是否已经被回收,如果已经回收说明activity能够被GC,直接返回即可。
3、 如果Activity没有被回收,调用GcTigger.runGc方法运行GC,GC完成后在运行第1步,然后运行第2步判断Activity是否被回收了,如果这时候还没有被回收,那就说明Activity可能已经泄露。
4、 如果Activity泄露了,就抓取内存dump文件(Debug.dumpHprofData)
5、 之后通过HeapAnalyzerService.runAnalysis进行分析内存文件分析
接着通过HeapAnalyzer(checkForLeak—findLeakingReference---findLeakTrace)来进行内存泄漏分析。
6、 最后通过DisplayLeakService进行内存泄漏的展示。