leakcanary源码分析

写在前面

leakcanary版本是2.4.0

用法很简单, 就不再说了.
使用完后, 有几个问题

  1. 为何桌面会多出来一个Leaks的app icon? 是安装了一个app吗?
  2. 怎么监听Activity的?
  3. 怎么判断对象泄漏的?

前置知识

WeakReference和ReferenceQueue

WeakReference

gc时, 不管内存空间是否足够, 都会回收对象的内存.

ReferenceQueue

WeakReference有一个两参的构造函数WeakReference#WeakReference(T, java.lang.ref.ReferenceQueue), 第二个参数就是ReferenceQueue. 它的作用是, 如果gc时, 该对象成功被垃圾回收器回收, 那系统将会把他放到ReferenceQueue中.也即是, 提供了一个机制, 可以告知你, 哪些对象被回收. 注意: ReferenceQueue中出现的是被回收的对象.

为何桌面会多出来一个Leaks的app icon

这个Leaks的app icon并不是另外安装的apk, 而是系统提供的机制, 允许同一个app在桌面上有多个入口. 其使用方法可以参考这里
leakcanary的中使用的是activity-alias
leakcanary-android-core/src/main/AndroidManifest.xml如下

    

    
      
        
        
        
        
      
    

流程梳理

启动

leakcanary不需要在Application中初始化, 我们应该可以想到, leakcanary用的是ContentProvider
所以, 我们在源码AndroidManifest.xml文件中找ContentProvider
leakcanary-android-process/src/main/AndroidManifest.xml如下



  
    

    
  


leakcanary.internal.AppWatcherInstaller#onCreate
  override fun onCreate(): Boolean {
    val application = context!!.applicationContext as Application
    AppWatcher.manualInstall(application)
    return true
  }

AppWatcher.manualInstall就是其初始化逻辑.
顺着APP的调用链, 最终执行了leakcanary.internal.InternalAppWatcher#install

leakcanary.internal.InternalAppWatcher#install
  fun install(application: Application) {
    //如果不是主线程, 就抛出异常
    checkMainThread()
    //如果已经初始化过, 就不再初始化
     //防止重复初始化
    if (this::application.isInitialized) {
      return
    }
    SharkLog.logger = DefaultCanaryLog()
    InternalAppWatcher.application = application

    val configProvider = { AppWatcher.config }
    //监听Activity
    ActivityDestroyWatcher.install(application, objectWatcher, configProvider)
    //监听Fragment
    FragmentDestroyWatcher.install(application, objectWatcher, configProvider)
    onAppWatcherInstalled(application)
  }

这是我们遇到的第一个重要方法.
其实里面的大部分方法在第一遍的时候都不需要细看, 我们先抓主干.
重点是ActivityDestroyWatcher.install和FragmentDestroyWatcher.install

ActivityDestroyWatcher.install

leakcanary.internal.ActivityDestroyWatcher的代码很简单

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)
    }
  }
}

简单来说就是

        Application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityDestroyed(@NonNull Activity activity) {
                //既然这个Activity执行了onDestroy, 那它就该被回收
                //如果没被回收, 那该Activity就泄漏了
            }
        });

小结一下:

  1. 使用Application#registerActivityLifecycleCallbacks方法监听Activity的销毁, Activity执行onDestroy后, 开始监听该Activity
  2. 监听逻辑在ObjectWatcher#watch
leakcanary.internal.FragmentDestroyWatcher#install

FragmentDestroyWatcher#install与ActivityDestroyWatcher.install逻辑类似, 但复杂点

  fun install(
    application: Application,
    objectWatcher: ObjectWatcher,
    configProvider: () -> AppWatcher.Config
  ) {
    val fragmentDestroyWatchers = mutableListOf<(Activity) -> Unit>()

   //AndroidOFragmentDestroyWatcher
    if (SDK_INT >= O) {
      fragmentDestroyWatchers.add(
          AndroidOFragmentDestroyWatcher(objectWatcher, configProvider)
      )
    }
    //AndroidXFragmentDestroyWatcher
    getWatcherIfAvailable(
        ANDROIDX_FRAGMENT_CLASS_NAME,
        ANDROIDX_FRAGMENT_DESTROY_WATCHER_CLASS_NAME,
        objectWatcher,
        configProvider
    )?.let {
      fragmentDestroyWatchers.add(it)
    }
    //AndroidSupportFragmentDestroyWatcher
    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)
        }
      }
    })
  }

类型判断, 分别交给AndroidOFragmentDestroyWatcher/AndroidXFragmentDestroyWatcher/AndroidSupportFragmentDestroyWatcher处理

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)
  }
}

看到这里也许有点蒙圈, 简单来说, 就是监听Activity的销毁, 在Activity销毁的时候, 通过FragmentManager#registerFragmentLifecycleCallbacks监听Fragment的销毁, 在Fragment执行onFragmentDestroyed和onFragmentViewDestroyed的时候, 触发监听Fragment.
用代码表示就是:

        application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {

            @Override
            public void onActivityDestroyed(@NonNull Activity activity) {
                activity.getFragmentManager().registerFragmentLifecycleCallbacks(new FragmentManager.FragmentLifecycleCallbacks() {
                    @Override
                    public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {
                        super.onFragmentViewDestroyed(fm, f);
                        //监听f.getView()
                    }

                    @Override
                    public void onFragmentDestroyed(FragmentManager fm, Fragment f) {
                        super.onFragmentDestroyed(fm, f);
                        //监听fragment
                    }
                });
            }
        });

监听的方法跟监听Activity一样是leakcanary.ObjectWatcher#watch
在分析leakcanary.ObjectWatcher#watch之前, 我们还有一个重要的方法得分析
leakcanary.internal.InternalAppWatcher#install方法的最后一行

onAppWatcherInstalled(application)

onAppWatcherInstalled是InternalLeakCanary
onAppWatcherInstalled(application)实际执行的是leakcanary.internal.InternalLeakCanary#invoke

leakcanary.internal.InternalLeakCanary#invoke
 override fun invoke(application: Application) {
    _application = application

    checkRunningInDebuggableBuild()

    //重要的是这里
    //注册了监听
    //回调方法是onObjectRetained
    //后面会用到
    AppWatcher.objectWatcher.addOnObjectRetainedListener(this)

    val heapDumper = AndroidHeapDumper(application, createLeakDirectoryProvider(application))
    //构造GC工具
    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
    )
    //...
  }
leakcanary.ObjectWatcher#watch
  @Synchronized fun watch(
    watchedObject: Any,
    description: String
  ) {
     //...
    //生成弱引用对象
    //注意最后一个参数queue, 就是我们前面讲的ReferenceQueue
    val reference =
      KeyedWeakReference(watchedObject, key, description, watchUptimeMillis, queue)
    //...

    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()
      //注意这里执行了onObjectRetained, 也就是上面leakcanary.internal.InternalLeakCanary#onObjectRetained方法
      onObjectRetainedListeners.forEach { it.onObjectRetained() }
    }
  }

所以, 接下来我们看看leakcanary.internal.InternalLeakCanary#onObjectRetained

leakcanary.internal.InternalLeakCanary#onObjectRetained
  override fun onObjectRetained() {
    if (this::heapDumpTrigger.isInitialized) {
      heapDumpTrigger.onObjectRetained()
    }
  }

查看调用链, 最后执行了leakcanary.internal.HeapDumpTrigger#checkRetainedObjects
!!!核心方法来了

  private fun checkRetainedObjects(reason: String) {
    //...
    var retainedReferenceCount = objectWatcher.retainedObjectCount
    //如果没被回收的对象数大于0, 就是仍然有对象没被回收
    if (retainedReferenceCount > 0) {
      //执行GC
      gcTrigger.runGc()
      retainedReferenceCount = objectWatcher.retainedObjectCount
    }
    //如果GC后都被回收了
    if (checkRetainedCount(retainedReferenceCount, config.retainedVisibleThreshold)) return
    //...
    //一堆防止误判的逻辑
 
    //生成heap文件
    dumpHeap(retainedReferenceCount, retry = true)
  }
leakcanary.GcTrigger.Default#runGc
    override fun runGc() {
      //执行gc
      Runtime.getRuntime().gc()
      enqueueReferences()
      //强制调用已经失去引用的对象的finalize方法 
      System.runFinalization()
    }
leakcanary.internal.HeapDumpTrigger#dumpHeap
  private fun dumpHeap(
    retainedReferenceCount: Int,
    retry: Boolean
  ) {
    //...生成heapDump文件
    val heapDumpFile = heapDumper.dumpHeap()
    //...开启Service分析heapDump文件
    HeapAnalyzerService.runAnalysis(application, heapDumpFile)
  }

小结:

  1. 监听时机: Activity执行onDestroy的时候
  2. 发现泄漏对象:WeakReference+ReferenceQueue, 触发GC, 没有出现在ReferenceQueue中的就是泄漏的对象.
  3. 分析泄漏对象:生成heapDumpFile文件, 调用HeapAnalyzerService分析该文件

下面我们就来看看, 如何生成heapDumpFile , 以及如何分析heapDumpFile .
在上面的leakcanary.internal.HeapDumpTrigger#dumpHeap方法中, 我们可以看到使用leakcanary.internal.HeapDumper#dumpHeap生成heapDumpFile, 而heapDumper是接口, 其具体实现是leakcanary.internal.AndroidHeapDumper, 所以我们就来看看leakcanary.internal.AndroidHeapDumper#dumpHeap

leakcanary.internal.AndroidHeapDumper#dumpHeap
  override fun dumpHeap(): File? {
    //...
    return try {
      //实际上就是调用了系统的android.os.Debug#dumpHprofData(java.lang.String)
      Debug.dumpHprofData(heapDumpFile.absolutePath)
      //...
    }
  }

我们可以看到, 生成heapDumpFile文件实际上是调用了系统的方法Debug#dumpHprofData, 参数是文件路径
知道了如何生成heapDumpFile, 我们来看最后一步, 分析heapDumpFile, 也即是, 找到泄漏对象的调用栈.

leakcanary.internal.HeapAnalyzerService

首先调用的就是leakcanary.internal.HeapAnalyzerService.Companion#runAnalysis, 启动Service

leakcanary.internal.HeapAnalyzerService.Companion#runAnalysis
    fun runAnalysis(
      context: Context,
      heapDumpFile: File
    ) {
      val intent = Intent(context, HeapAnalyzerService::class.java)
      intent.putExtra(HEAPDUMP_FILE_EXTRA, heapDumpFile)
      startForegroundService(context, intent)
    }

    private fun startForegroundService(
      context: Context,
      intent: Intent
    ) {
      if (SDK_INT >= 26) {
        context.startForegroundService(intent)
      } else {
        // Pre-O behavior.
        context.startService(intent)
      }
    }

方法很简单, 就是启动HeapAnalyzerService, 如果API大于26, 调用context.startForegroundService, 否则 context.startService

接下来, 我们看下HeapAnalyzerService
HeapAnalyzerService是IntentService的子类, IntentService自带线程(HandlerThread), 通过一系列封装, 执行Service#onStart和Service#onStartCommand时, 都会在子线程(HandlerThread)中执行HeapAnalyzerService#onHandleIntentInForeground
所以, 我们需要看的是HeapAnalyzerService#onHandleIntentInForeground

HeapAnalyzerService#onHandleIntentInForeground
  override fun onHandleIntentInForeground(intent: Intent?) {
     //设置线程优先级
    // 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()) {
      //开始分析heapDumpFile文件
      analyzeHeap(heapDumpFile, config)
    } else {
      missingFileFailure(heapDumpFile)
    }
    onAnalysisProgress(REPORTING_HEAP_ANALYSIS)
    config.onHeapAnalyzedListener.onHeapAnalyzed(heapAnalysis)
  }

analyzeHeap方法实际调用HeapAnalyzer#analyze

HeapAnalyzer#analyze
  fun analyze(
    heapDumpFile: File,
    leakingObjectFinder: LeakingObjectFinder,
    referenceMatchers: List = emptyList(),
    computeRetainedHeapSize: Boolean = false,
    objectInspectors: List = emptyList(),
    metadataExtractor: MetadataExtractor = MetadataExtractor.NO_OP,
    proguardMapping: ProguardMapping? = null
  ): HeapAnalysis {
    //... 检查heapDumpFile文件是否存在, 不存在就不用分析了, 直接失败
    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) {
      //... 分析失败
    }
  }
Hprof.Companion#open
    fun open(hprofFile: File): Hprof {
      //... 简单描述就是通过okio读文件, 生成Hprof

      return Hprof(
          channel, source, reader, heapDumpTimestamp, hprofVersion, fileLength
      )
    }
HprofHeapGraph.Companion#indexHprof

HprofHeapGraph.Companion#indexHprof -> HprofInMemoryIndex.Companion#createReadingHprof -> HprofReader#readHprofRecords
在HprofReader#readHprofRecords中, 对文件进行了解析

HprofReader#readHprofRecords
  fun readHprofRecords(
    recordTypes: Set>,
    listener: OnHprofRecordListener
  ) {
    //...
    //使用while对文件进行解析
    //解析各个tag
    //生成对应信息
    while (!exhausted()) {
      // type of the record
      val tag = readUnsignedByte()
      when (tag) {
        STRING_IN_UTF8 -> {
          if (readStringRecord) {
            val recordPosition = position
            val id = readId()
            val stringLength = length - identifierByteSize
            val string = readUtf8(stringLength)
            val record = StringRecord(id, string)
            listener.onHprofRecord(recordPosition, record)
          } else {
            skip(length)
          }
        }
        //... 与STRING_IN_UTF8 同理, 同样是对各个tag进行分类解析       
      }
    }
  }

解析到文件数据以后, 开始对数据查找泄漏对象的调用栈信息. 生成各个调用栈信息并保存在classIndex/instanceIndex/objectArrayIndex/primitiveArrayIndex几个SortedBytesMap中
注意, 这里的listener是HprofInMemoryIndex.Builder
在HprofInMemoryIndex.Builder#onHprofRecord方法中, 可以看到, 其解析并保存了全部的GCRoot对象

override fun onHprofRecord(
      position: Long,
      record: HprofRecord
    ) {
      when (record) {
        //..其它类型对象
        is GcRootRecord -> {
          val gcRoot = record.gcRoot
          if (gcRoot.id != ValueHolder.NULL_REFERENCE
              && indexedGcRootsTypes.contains(gcRoot.javaClass)
          ) {
            gcRoots += gcRoot
          }
        }      
        //..其它类型对象
      }
    }
HeapAnalyzer#analyzeGraph
  private fun FindLeakInput.analyzeGraph(
    metadataExtractor: MetadataExtractor,
    leakingObjectFinder: LeakingObjectFinder,
    heapDumpFile: File,
    analysisStartNanoTime: Long
  ): HeapAnalysisSuccess {
    //...
    //找到泄漏对象的id
    //leakingObjectFinder是接口, 实际实现是FilteringLeakingObjectFinder
    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
    )
  }
HeapAnalyzer#findLeaks
  private fun FindLeakInput.findLeaks(leakingObjectIds: Set): Pair, List> {
    val pathFinder = PathFinder(graph, listener, referenceMatchers)
    val pathFindingResults =
      pathFinder.findPathsFromGcRoots(leakingObjectIds, computeRetainedHeapSize)
    //...
    return buildLeakTraces(pathFindingResults)
  }

shark.internal.PathFinder#findPathsFromGcRoots(shark.internal.PathFinder.State) -> shark.HprofHeapGraph#findObjectById -> shark.internal.HprofInMemoryIndex#indexedObjectOrNull
HprofInMemoryIndex#indexedObjectOrNull 的内容比较简单, 就是根据objectId从前面存的classIndex/instanceIndex/objectArrayIndex/primitiveArrayIndex中取数据, 能从哪个中取到就直接返回

最后一步就是shark.HeapAnalyzer#buildLeakTraces方法构建调用栈信息了.

总结

  1. 触发监听
    Activity执行onDestroy时, 触发对Activity的监听.(android.app.Application#registerActivityLifecycleCallbacks)
    Fragment执行onDestroyView时触发对Fragment的监听(FragmentManager#registerFragmentLifecycleCallbacks)
  2. 发现泄漏对象
    WeakReference+ReferenceQueue机制, 执行GC(GcTrigger.Default#runGc, 实际执行Runtime.getRuntime().gc())后, 如果有对象不在ReferenceQueue中, 就认为发生了泄漏
  3. 生成dumpHeap文件
    (android.os.Debug#dumpHprofData(java.lang.String)
  4. 解析文件(HeapAnalyzerService), 生成调用栈

其它

内存分析的实现机制这一块可以对比KOOM 以及 介绍

你可能感兴趣的:(leakcanary源码分析)