- LeakCanary 资源信息
https://github.com/square/leakcanary
debugImplementation ‘com.squareup.leakcanary:leakcanary-android:2.3’
1. 核心原理
1.1 强引用-无法回收
申请一个强引用对象,主动调用GC,查看此对象是否被回收。
private static void test1() throws InterruptedException {
ReferenceQueue referenceQueue = new ReferenceQueue();
Object object = new Object();
WeakReference weakReference = new WeakReference(object, referenceQueue);
System.out.println("Before GC, ref: " + weakReference.get());
System.out.println("Before GC, queue: " + referenceQueue.poll());
Runtime.getRuntime().gc();
Thread.sleep(300);
System.out.println("-----");
System.out.println("After GC, ref: " + weakReference.get());
System.out.println("After GC, queue: " + referenceQueue.poll());
}
因为object对象是一个强引用,new Object()的内存空间不会被回收,所以主动调用GC方法后,仍然可以获取object对象。
Before GC, ref: java.lang.Object@63947c6b
Before GC, queue: null
-----
After GC, ref: java.lang.Object@63947c6b
After GC, queue: null
1.2 若引用-可回收
初始化一个弱引用,并与引用队列关联,调用GC后,弱引用对象被回收,进入引用队列。
private static void test2() throws InterruptedException {
ReferenceQueue referenceQueue = new ReferenceQueue();
WeakReference weakReference = new WeakReference(new Object(), referenceQueue);
System.out.println("Before GC, ref: " + weakReference.get());
System.out.println("Before GC, queue: " + referenceQueue.poll());
Runtime.getRuntime().gc();
Thread.sleep(300);
System.out.println("-----");
System.out.println("After GC, ref: " + weakReference.get());
System.out.println("After GC, queue: " + referenceQueue.poll());
}
改为弱引用的方式,主动GC后,object对象被回收了,并且添加到了引用队列中。
Before GC, ref: java.lang.Object@2b193f2d
Before GC, queue: null
-----
After GC, ref: null
After GC, queue: java.lang.ref.WeakReference@355da254
2.流程
2.1 初始化流程
2.x版本的Leak Canary,用户无需进行初始化这一步骤,SDK内部在ContentProvider中主动进行了初始化操作。
leakcanary/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>
internal sealed class AppWatcherInstaller : ContentProvider() {
...
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
InternalAppWatcher.install(application)
return true
}
...
}
InstallAppWatcher.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 and Fragment 自动监控的原理 ObjectWatcher#watch()
Activity#onDestroy()
Fragment#onDestroyView() 、Fragment#onDestroy()
检测时机:
app从不可见到可见
从可见到不可见
watch方法调用
LeakCanary可以监控任意对象的内存泄漏,主动调用watch方法即可
Object object = new Object();
AppWatcher.INSTANCE.getObjectWatcher().watch(object, "object");
2.2 检测流程
ObjectWatcher#watch(obj, desc)
@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)
}
}
InternalLeakCanary#onObjectRetained()
override fun onObjectRetained() {
if (this::heapDumpTrigger.isInitialized) {
heapDumpTrigger.onObjectRetained()
}
}
HeapDumpTrigger#onObjectRetained()
fun onObjectRetained() {
scheduleRetainedObjectCheck(
reason = "found new object retained",
rescheduling = false
)
}
HeapDumpTrigger#checkRetainedObjects()
private fun checkRetainedObjects(reason: String) {
...
gcTrigger.runGc()
...
}
HeapDumpTrigger#dumpHeap()
3. 总结
- 初始化流程
- 检测流程