LeakCanary源码分析
参考资料:
三方库源码笔记(5)-LeakCanary 源码详解
关于Java中的WeakReference
一.在哪里初始化
在老版本的LeakCanary中,在集成LeakCanary时需要我们在Application中调用LeakCanary.install(this);进行初始化工作。但是在新版本中只需要我们添加依赖后,就可以直接监听Activity,Fragment等的内存泄露问题了。那么它的初始化工作是在那执行的呢?
它在 AppWatcherInstaller类的 onCreate()自动完成了初始化工作。
internal sealed class AppWatcherInstaller : ContentProvider() {
override fun onCreate(): Boolean {
val application = context!!.applicationContext as Application
AppWatcher.manualInstall(application)
return true
}
AppWatcherInstaller类是ContentProvider的一个子类对象。
这块说一下小知识,那就是ContentProvider的onCreate方法执行顺序是先于Application的onCreate的,晚于Application的attachBaseContext方法。 在这里它会调用到manualInstall()方法,完成初始化的工作。
在manualInstall方法中,他会遍历watchersToInstall集合调用每一个对象的install方法完成生命周期的绑定等初始化工作。
可以看到watchersToInstall集合中默认有四个对象 ActivityWatcher,FragmentAndViewModelWatcher,RootViewWatcher,ServiceWatcher,即对应这LeakCanary默认完成Activity,Fragment,RootView,Service的内存泄露检测,如果需要检测其他的对象,需要手动添加并处理。
@JvmOverloads
fun manualInstall(
application: Application,
retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5),
watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application)
) {
//遍历watchersToInstall集合,完成每一个对象的初始化工作。
watchersToInstall.forEach {
it.install()
}
}
//appDefaultWatchers(application)
fun appDefaultWatchers(
application: Application,
reachabilityWatcher: ReachabilityWatcher = objectWatcher
): List<InstallableWatcher> {
return listOf(
ActivityWatcher(application, reachabilityWatcher),
FragmentAndViewModelWatcher(application, reachabilityWatcher),
RootViewWatcher(reachabilityWatcher),
ServiceWatcher(reachabilityWatcher)
)
}
二. 生命周期的监听
以Activity为例来说明:
知识补充:Activity声明周期监听器
ActivityLifecycleCallbacks这个是Android给我们提供的一个检测Activity生命周期的一个接口,我们可以实现这个接口,重写他的方法之后,通过Application.registerActivityLifecycleCallbacks()方法注册下这个callback,就可以监听Activity生命周期回调了,他的回调是和Activity生命周期一样的(多了一个onActivitySaveInstanceState)。
值得注意的是:使用ActivityLifecycleCallbacks来监听生命周期会对所有的Activity都做监听。
用一个demo证明一下:在Application中调用如下的LifeCycle()方法。在MainActivity中并跳转到MainActivity2,然后回退到MainActivity观察打印情况。
private fun LifeCycle() {
val lifecycleCallbacks = object : Application.ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
Log.d(TAG, "onActivityCreated: ${activity.hashCode()}")
}
override fun onActivityStarted(activity: Activity) {
Log.d(TAG, "onActivityStarted: ${activity.hashCode()}")
}
.......
override fun onActivityDestroyed(activity: Activity) {
Log.d(TAG, "onActivityDestroyed: ${activity.hashCode()}")
}
}
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
打印结果:
2021-07-20 20:18:01.854 7642-7642/com.example.leakcanary D/LifeCycleCall: onActivityCreated: 201342997
2021-07-20 20:18:02.035 7642-7642/com.example.leakcanary D/LifeCycleCall: onActivityStarted: 201342997
2021-07-20 20:18:07.028 7642-7642/com.example.leakcanary D/LifeCycleCall: onActivityCreated: 100570917
2021-07-20 20:18:07.048 7642-7642/com.example.leakcanary D/LifeCycleCall: onActivityStarted: 100570917
2021-07-20 20:18:09.224 7642-7642/com.example.leakcanary D/LifeCycleCall: onActivityStarted: 201342997
2021-07-20 20:18:09.825 7642-7642/com.example.leakcanary D/LifeCycleCall: onActivityDestroyed: 100570917
可以看到:符合生命周期流程,且所有的Activity都会被监听到。
ActivityWatcher源码分析
可以看到,在它的install()方法中,会调用registerActivityLifecycleCallbacks方法将重写好的ActivityLifecycleCallback对象传入进入,这个回调对象主要重写了onActivityDestroyed方法。从而就接管了Activity的生命周期。然后当当前的Activity回调onDestroy方法后进行后续判断看是否发生内存泄露。
class ActivityWatcher(
private val application: Application,
private val reachabilityWatcher: ReachabilityWatcher
) : InstallableWatcher {
private val lifecycleCallbacks =
object : Application.ActivityLifecycleCallbacks by noOpDelegate() {
override fun onActivityDestroyed(activity: Activity) {
reachabilityWatcher.expectWeaklyReachable(
activity, "${activity::class.java.name} received Activity#onDestroy() callback"
)
}
}
override fun install() {
//注册,在lifecycleCallbacks中拿到各个生命周期的回调
application.registerActivityLifecycleCallbacks(lifecycleCallbacks)
}
override fun uninstall() {
application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks)
}
}
三. 如何判断是否发生内存泄露
知识补充:WeakReference(弱引用)
弱引用也是用来描述非必需的对象,但是强度比软引用更弱。不管系统内存是否充足,在下一次垃圾回收发生时,系统都会回收掉只被弱引用关联的对象。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。
用一个demo来说明:
Apple类只有一个name字段。
class Apple(var name: String) {
/**
* 覆盖finalize,在回收的时候会执行。
* @throws Throwable
*/
@Throws(Throwable::class)
protected fun finalize() {
println("Apple: $name finalize。")
}
override fun toString(): String {
return "Apple{" +
"name='" + name + '\'' +
'}' + ", hashCode:" + this.hashCode()
}
}
fun main(){
val appleReferenceQueue = ReferenceQueue<Apple>()
//appleWeakReference,appleWeakReference2是弱引用对象 ,他们的引用是 Apple("青苹果"),Apple("毒苹果")
val appleWeakReference = WeakReference(Apple("青苹果"), appleReferenceQueue)
val appleWeakReference2 = WeakReference(Apple("毒苹果"), appleReferenceQueue)
println("=====gc调用前=====")
var reference: Reference<out Apple?>?
while (appleReferenceQueue.poll().also { reference = it } != null) {
//不会输出,因为没有回收被弱引用的对象,并不会加入队列中
System.out.println(reference)
}
println(appleWeakReference) //java.lang.ref.WeakReference@2d6e8792
println(appleWeakReference2) //java.lang.ref.WeakReference@2812cbfa
println(appleWeakReference.get()) //Apple{name='青苹果'}, hashCode:718231523
println(appleWeakReference2.get()) //Apple{name='毒苹果'}, hashCode:1349414238
println("=====调用gc=====")
System.gc()
try {
Thread.sleep(5000)
} catch (e: InterruptedException) {
e.printStackTrace()
}
println("=====gc调用后=====")
println(appleWeakReference.get()) //null
println(appleWeakReference2.get()) //null
//输出结果,并且就是上面的appleWeakReference、appleWeakReference2,再次证明对象被回收了
var reference2: Reference<out Apple?>? = null
while (appleReferenceQueue.poll().also { reference2 = it } != null) {
//如果使用继承的方式就可以包含其他信息了
println("appleReferenceQueue中:$reference2")
}
}
说明,上面定义了两个弱引用对象 appleWeakReference,appleWeakReference2 。在gc之前,被弱引用的对象即Apple实例没有被回收,所以引用队列为空。当发生gc后,因为 我们的Apple实例都只被弱引用关联所以会被回收,并执行我们重写的finalize()方法。此时被弱引用的对象变为空。并且弱引用的对象会被加入到与之关联的弱引用队列。
LeakCanary判断是否内存泄露
LeakCanary判断是否内存泄露的机制就是借助上述过程完成的。用Activity来说明一下。因为在Activity中声明了周期监听器,当监听到ActivityDestroyed事件发生时,就会用当前Activity创建一个弱引用对象,并和定义的引用队列保持联系。然后为Activity生成一个唯一的key。以键值对的形式 把key和生成的弱引用对象保存在map集合中。在一段时间后(可以主动触发几次 GC)检测 引用队列是否有值,如果有值,则删除集合中对应的对象。最后集合中剩下的就是可能发生内存泄露的。
具体过程如下:
-
expectWeaklyReachable方法
在刚才的onActivityDestroyed方法中,会调用到 reachabilityWatcher.expectWeaklyReachable()方法,这个方法的具体实现在ObjectWatcher。它里面做的工作就是生成当前Activity的弱引用对象,并加入到map集合中,然后在线程池中调用moveToRetained(key)方法。
class ObjectWatcher constructor{ private val onObjectRetainedListeners = mutableSetOf<OnObjectRetainedListener>() //保存弱引用的集合 private val watchedObjects = mutableMapOf<String, KeyedWeakReference>() //引用队列 private val queue = ReferenceQueue<Any>() fun watch( watchedObject: Any, description: String ) { expectWeaklyReachable(watchedObject, description) } @Synchronized override fun expectWeaklyReachable( watchedObject: Any, description: String ) { removeWeaklyReachableObjects() val key = UUID.randomUUID() //获取当前Activity对应的key .toString() val watchUptimeMillis = clock.uptimeMillis() //生成弱引用对象 val reference = KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue) //把弱引用对象加入到map集合 watchedObjects[key] = reference //通过线程池调用了moveToRetained方法开始判断是否发生了内存泄漏 checkRetainedExecutor.execute { moveToRetained(key) } }
关于这个线程池:
val objectWatcher = ObjectWatcher( clock = { SystemClock.uptimeMillis() }, checkRetainedExecutor = { mainHandler.postDelayed(it, retainedDelayMillis) }, isEnabled = { true } ){} fun manualInstall( application: Application, retainedDelayMillis: Long = TimeUnit.SECONDS.toMillis(5), watchersToInstall: List<InstallableWatcher> = appDefaultWatchers(application) ) { ...... this.retainedDelayMillis = retainedDelayMillis }
通过主线程的Handler的postDelay延迟5s再去判断是否发生了内存泄露。(5s足够GC了)
-
moveToRetained方法
刚才gc过后,如果没有发生内存泄露,那么Activity对象也就是被弱引用的对象会被回收,而对应的弱引用对象会被加入到引用队列中。removeWeaklyReachableObjects()方法就是从引用队列拿到弱引用对象,然后删除map集合中对应的元素,结束。
如果发生内存泄露,那么正好和上面相反,此时集合中就存在Activity对应的弱引用对象,就会调用到onObjectRetained()方法。
@Synchronized private fun moveToRetained(key: String) { removeWeaklyReachableObjects() val retainedRef = watchedObjects[key] if (retainedRef != null) { retainedRef.retainedUptimeMillis = clock.uptimeMillis() onObjectRetainedListeners.forEach { it.onObjectRetained() } } } private fun removeWeaklyReachableObjects() { var ref: KeyedWeakReference? do { ref = queue.poll() as KeyedWeakReference? if (ref != null) { watchedObjects.remove(ref.key) } } while (ref != null) }
-
检测留存的对象
刚才的一遍判断结果可能还不准确。onObjectRetained()方法调用后,会回调到InternalLeakCanary.onObjectRetained方法。然后进入checkRetainedObjects()方法进行后续检测。
- 第一次移除不可达对象:移除 ReferenceQueue 中记录的KeyedWeakReference 对象(引用着监听的对象实例);
- 主动触发GC:回收不可达的对象;
- 第二次移除不可达对象:经过一次GC后可以进一步导致只有WeakReference持有的对象被回收,因此再一次移除ReferenceQueue 中记录的KeyedWeakReference 对象;
- 判断是否还有剩余的监听对象存活,且存活的个数是否超过阈值;
- 若满足上面的条件,则抓取Hprof文件,实际调用的是android原生的Debug.dumpHprofData(heapDumpFile.absolutePath) ;启动异步的HeapAnalyzerService 分析hprof文件,找到泄漏的GcRoot链路。
// HeapDumpTrigger.checkRetainedObjects private fun checkRetainedObjects() { //第一次移除不可达对象 var retainedReferenceCount = objectWatcher.retainedObjectCount //如果保存弱引用对象的map集合还有元素 if (retainedReferenceCount > 0) { //主动触发GC gcTrigger.runGc() //第二次移除不可达对象 retainedReferenceCount = objectWatcher.retainedObjectCount } //判断是否还有剩余的监听对象存活,且存活的个数是否超过阈值; if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return ...... }