LeakCanary原理分析

  • 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. 总结

  1. 初始化流程
  2. 检测流程
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值