内存泄漏+leakCanary

参考:https://carsonho.blog.csdn.net/article/details/79407707

       https://allenwu.itscoder.com/leakcanary-source

内存泄漏:

长期存活对象引用短期存活对象,导致短期存活对象占用的内存无法回收,这种现象称为内存泄漏。

内存溢出:

应用程序所需的内存超出了系统为其分配的内存限额的现象

常见的内存泄漏

  1. 集合类

    • 原因

      集合类添加元素后,仍引用着集合元素对象,导致该集合元素对象不可被回收,从而导致内存泄漏

    • 实例

      //通过 循环申请Object 对象 & 将申请的对象逐个放入到集合List
          List objectList = new ArrayList<>();        
             for (int i = 0; i < 10; i++) {
                  Object o = new Object();
                  objectList.add(o);
                  o = null;
              }
      //虽释放了集合元素引用的本身:o=null)
      //但集合List仍然引用该对象,故垃圾回收器GC 依然不可回收该对象
        
          
    • 解决

      //集合类 添加集合元素对象后,在使用后必须从集合中删除
      objectList.clear();
      objectList=null;
      
    • static关键字修饰的成员变量

      • 被 Static 关键字修饰的成员变量的生命周期 = 应用程序的生命周期

      • 原因

        若使被 Static 关键字修饰的成员变量 引用耗费资源过多的实例(如Context),则容易出现该成员变量的生命周期 > 引用实例生命周期的情况,当引用实例需结束生命周期销毁时,会因静态变量的持有而无法被回收,从而出现内存泄

      • 实例

        // 创建单例时,需传入一个Context
        // 若传入的是Activity的Context,此时单例 则持有该Activity的引用
        // 由于单例一直持有该Activity的引用(直到整个应用生命周期结束),即使该Activity退出,该Activity的内存也不会被回收
        // 特别是一些庞大的Activity,此处非常容易导致OOM
        
        public class SingleInstanceClass {    
            private static SingleInstanceClass instance;    
            private Context mContext;    
            private SingleInstanceClass(Context context) {        
                this.mContext = context; // 传递的是Activity的context
            }  
          
            public SingleInstanceClass getInstance(Context context) {        
                if (instance == null) {
                    instance = new SingleInstanceClass(context);
                }        
                return instance;
            }
        }
        //单例模式 由于其静态特性,其生命周期的长度 = 应用程序的生命周期
        //若1个对象已不需再使用 而单例对象还持有该对象的引用,那么该对象将不能被正常回收 从而 导致内存泄漏
        
      • 解决

        单例模式应该引用生命周期等于应用生命周期的对象。

        public class SingleInstanceClass {    
            private static SingleInstanceClass instance;    
            private Context mContext;    
            private SingleInstanceClass(Context context) {        
                this.mContext = context.getApplicationContext(); // 传递的是Application 的context
            }    
        
            public SingleInstanceClass getInstance(Context context) {        
                if (instance == null) {
                    instance = new SingleInstanceClass(context);
                }        
                return instance;
            }
        }
        
    • 非静态内部类/匿名类

      非静态内部类/匿名类默认持有外部类的引用;而静态内部类则不会

      常见3种情况,分别是:非静态内部类的实例 = 静态、多线程、消息传递机制(Handler)

      1. 非静态内部类的实例 = 静态

        • 原因

          若 非静态内部类所创建的实例 = 静态(其生命周期 = 应用的生命周期),会因 非静态内部类默认持有外部类的引用 而导致外部类无法释放,最终 造成内存泄露

        • 实例

          // 背景:
          // a. 在启动频繁的Activity中,为了避免重复创建相同的数据资源,会在Activity内部创建一个非静态内部类的单例
          // b. 每次启动Activity时都会使用该单例的数据
          
          public class TestActivity extends AppCompatActivity {  
              
              // 非静态内部类的实例的引用
              // 注:设置为静态  
              public static InnerClass innerClass = null; 
             
              @Override
              protected void onCreate(@Nullable Bundle savedInstanceState) {        
                  super.onCreate(savedInstanceState);   
          
                  // 保证非静态内部类的实例只有1个
                  if (innerClass == null)
                      innerClass = new InnerClass();
              }
          
              // 非静态内部类的定义    
              private class InnerClass {        
                  //...
              }
          }
          
          // 造成内存泄露的原因:
          // a. 当TestActivity销毁时,因非静态内部类单例的引用(innerClass)的生命周期 = 应用App的生命周期、持有外部类TestActivity的引用
          // b. 故 TestActivity无法被GC回收,从而导致内存泄漏
          
        • 解决:

          1. 将非静态内部类设置为:静态内部类(静态内部类默认不持有外部类的引用)
          2. 该内部类抽取出来封装成一个单例
          3. 尽量 避免 非静态内部类所创建的实例 = 静态

          ps:若需使用Context,建议使用 Application 的 Context

      2. 多线程:AsyncTask、实现Runnable接口、继承Thread类

        • 原因

          多线程的使用方法 = 非静态内部类 / 匿名类;即线程类属于非静态内部类 / 匿名类
          当工作线程正在处理任务,外部类需销毁时,由于工作线程实例持有外部类引用,将使得外部类无法被垃圾回收器(GC)回收,从而造成内存泄露

        • 实例

            /** 
             * 方式1:新建Thread子类(内部类)
             */  
              public class MainActivity extends AppCompatActivity {
          
              public static final String TAG = "carson:";
              @Override
              public void onCreate(Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                  setContentView(R.layout.activity_main);
          
                  // 通过创建的内部类 实现多线程
                  new MyThread().start();
          
              }
              // 自定义的Thread子类
              private class MyThread extends Thread{
                  @Override
                  public void run() {
                      try {
                          Thread.sleep(5000);
                          Log.d(TAG, "执行了多线程");
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
          
           /** 
             * 方式2:匿名Thread内部类
             */ 
              public class MainActivity extends AppCompatActivity {
          
              public static final String TAG = "carson:";
          
              @Override
              public void onCreate(Bundle savedInstanceState) {
                  super.onCreate(savedInstanceState);
                  setContentView(R.layout.activity_main);
          
                  // 通过匿名内部类 实现多线程
                  new Thread() {
                      @Override
                      public void run() {
                          try {
                              Thread.sleep(5000);
                              Log.d(TAG, "执行了多线程");
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
          
                      }
                  }.start();
              }
          }
          
          /** 
          * 分析:内存泄露原因
          */ 
          // 工作线程Thread类属于非静态内部类 / 匿名内部类,运行时默认持有外部类的引用
          // 当工作线程运行时,若外部类MainActivity需销毁
          // 由于此时工作线程类实例持有外部类的引用,将使得外部类无法被垃圾回收器(GC)回收,从而造成 内存泄露
          
        • 解决方案1:静态内部类不默认持有外部类的引用,从而使得 “工作线程实例 持有外部类引用” 的引用关系 不复存在将Thread的子类设置成静态内部类

          /** 
           * 解决方式1:静态内部类
           * 原理:静态内部类 不默认持有外部类的引用,从而使得 “工作线程实例 持有 外部类引用” 的引用关系 不复存在
           * 具体实现:将Thread的子类设置成 静态内部类
           */  
            public class MainActivity extends AppCompatActivity {
          
            public static final String TAG = "carson:";
            @Override
            public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);
          
                // 通过创建的内部类 实现多线程
                new MyThread().start();
          
            }
            // 分析1:自定义Thread子类
            // 设置为:静态内部类
            private static class MyThread extends Thread{
                @Override
                public void run() {
                    try {
                        Thread.sleep(5000);
                        Log.d(TAG, "执行了多线程");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
          }
          
          /** 
           * 解决方案2:当外部类结束生命周期时,强制结束线程
           * 原理:使得 工作线程实例的生命周期 与 外部类的生命周期 同步
           * 具体实现:当 外部类(此处以Activity为例) 结束生命周期时(此时系统会调用onDestroy()),强制结束线程(调用stop())
           */ 
           @Override
            protected void onDestroy() {
                super.onDestroy();
                Thread.stop();
                // 外部类Activity生命周期结束时,强制结束线程
            }
          
        • 解决方案2:使得工作线程实例的生命周期 外部类的生命周期同步

          当外部类(此处以Activity为例) 结束生命周期时(此时系统会调用onDestroy(),强制结束线程(调用stop())

           @Override
          protected void onDestroy() {
                 super.onDestroy();
                 Thread.stop();
                 // 外部类Activity生命周期结束时,强制结束线程
          }
          
      3. Handler机制

        • 解决方案1

          静态内部类+弱引用

          private static class FHandler extends Handler{
                  // 定义 弱引用实例
                  private WeakReference reference;
                  // 在构造方法中传入需持有的Activity实例
                  public FHandler(Activity activity) {
                      // 使用WeakReference弱引用持有Activity实例
                      reference = new WeakReference(activity); 
                  }
                  // 通过复写handlerMessage() 从而确定更新UI的操作
                  @Override
                  public void handleMessage(Message msg) {
                      switch (msg.what) {
                          case 1:
                              Log.d(TAG, "收到线程1的消息");
                              break;
                          case 2:
                              Log.d(TAG, " 收到线程2的消息");
                              break;
                      }
                  }
              }
          //要使用静态内部类,不然可能会造成内存泄露。原因是非静态内部类会持有外部类的引用,而Handler发出的Message会持有Handler的引用。如果这个Message是个延迟的消息,此时activity被退出了,但Message依然在“流水线”上,Message->handler->activity(可达性分析法),那么activity就无法被回收,导致内存泄露。
          
        • 解决方案2

          当外部类结束生命周期时,清空Handler内消息队列

          @Override
          protected void onDestroy() {
                 super.onDestroy();
                 mHandler.removeCallbacksAndMessages(null);
                 // 外部类Activity生命周期结束时,同时清空消息队列 & 结束Handler生命周期
          }
          
      4. 资源使用后未关闭

        • 原因:

          对于资源的使用(如广播BraodcastReceiver、文件流File、数据库游标Cursor、图片资源Bitmap等),若在Activity销毁时无及时关闭/注销这些资源,则这些资源将不会被回收,从而造成内存泄漏。

        • 解决:

          // 对于 广播BraodcastReceiver:注销注册
          unregisterReceiver()
          
          // 对于 文件流File:关闭流
          InputStream / OutputStream.close()
          
          // 对于数据库游标cursor:使用后关闭游标
          cursor.close()
          
          // 对于 图片资源Bitmap:Android分配给图片的内存只有8M,若1个Bitmap对象占内存较多,当它不再被使用时,应调用recycle()回收此对象的像素所占用的内存;最后再赋为null 
          Bitmap.recycle();
          Bitmap = null;
          
          // 对于动画(属性动画)
          // 将动画设置成无限循环播放repeatCount = “infinite”后
          // 在Activity退出时记得停止动画
          
      5. 其他

        图片已经损坏 :<
    • LeakCanary

      参考:https://zhuanlan.zhihu.com/p/57425510

      官方原理解释

      正常情况下一个Activity在执行Destroy之后就要销毁,LeakCanary做的就是在一个Activity 被Destroy之后将它放在一个WeakReference中,然后将这个WeakReference关联到一个ReferenceQueue,查看ReferenceQueue队列是否存在这个Activity的引用,如果不在这个队列中,执行一些GC清洗操作,再次查看。如果仍然不存在则证明该Activity泄漏了,之后Dump出heap信息,并用haha这个开源库去分析泄漏路径。

      基本原理
      1. LeakCanary通过 install() 方法初始化,主要是构造出一个RefWatcher,来监听Activity(ActivityRefWatcher) 或者Fragment(FragmentRefWatcher)

      2. 监听Actvity,原理在于 Application 的registerActivityLifecycleCallbacks(),该方法可以对应用内所有 Activity 的生命周期做监听, LeakCanary只监听了Destroy()方法。
        在每个Activity的OnDestroy()方法中都会回调refWatcher.watch()方法。

      3. watch()方法中先随机生成一个数作为key放在一个叫retainedKeys的set容器里面,用来区分待分析对象是否被回收。将弱引用和引用队列ReferenceQueue联合使用,如果弱引用持有的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
        即 KeyedWeakReference持有的Activity对象如果被垃圾回收,该对象就会加入到引用队列中。

      4. 避免因为gc不及时带来的误判,leakcanay会进行二次确认进行保证。首先清除此时已经到 RQ 的弱引用,调用 removeWeaklyReachableReferences()
        把已被回收的对象的 key 从 retainedKeys 移除,剩下的 key 都是未被回收的对象。

      5. 通过gone(reference)判断当前弱引用对应的Activity是否已经被回收,如果已经回收说明activity没有泄漏,直接返回即可。

      6. 如果当前检测对象没有改变其可达状态,则进行手动GC,再次清除已经到 RQ的弱引用,调用 removeWeaklyReachableReferences();如果此时对象还没有到队列,则此时已经可能泄漏。

      7. 将heap信息dump出来,抓取dump文件。

      8. HeapAnalyzerService被开启在一个独立的进程中,并且HeapAnalyzer使用了HAHA开源库解析了指定时刻的堆栈快照文件heap dump。

      9. 从heap dump中,HeapAnalyzer根据一个独特的引用key找到了KeyedWeakReference,并且定位了泄露的引用。

      10. HeapAnalyzer为了确定是否有泄露,计算了到GC Roots的最短强引用路径,然后建立了导致泄露的链式引用。

      11. 这个结果被传回到app进程中的DisplayLeakService,然后一个泄露通知便展现出来了。

      源码分析(版本二更深入)
      版本一
      创建RefWatcher
      public final class LeakCanary {
      
          public static @NonNull RefWatcher install(@NonNull Application application) {
              return refWatcher(application) // 创建AndroidRefWatcherBuilder对象
                  .listenerServiceClass(DisplayLeakService.class) // 配置监听分析结果的服务
                  .excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) // 配置排除的系统泄露
                  .buildAndInstall(); // 创建一个Refwatcher并监听Activity的引用
          }
          // ...
      }
      
      AndroidRefWatcherBuilder#buildAndInstall
      public final class AndroidRefWatcherBuilder extends RefWatcherBuilder {
      
          public @NonNull RefWatcher buildAndInstall() {
              if (LeakCanaryInternals.installedRefWatcher != null) {
                  throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
              }
              // 创建RefWatcher对象
              RefWatcher refWatcher = build();
              if (refWatcher != DISABLED) {
                  if (enableDisplayLeakActivity) {
                      LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
                  }
                  if (watchActivities) {
                      // 监听Activity的引用
                      ActivityRefWatcher.install(context, refWatcher);
                  }
                  if (watchFragments) {
                      // 监听Fragment的引用
                      FragmentRefWatcher.Helper.install(context, refWatcher);
                  }
              }
              LeakCanaryInternals.installedRefWatcher = refWatcher;
              return refWatcher;
          }
        // ...
      }
      
      监听Activity的引用
      ActivityRefWatcher
      public final class ActivityRefWatcher {
      
          public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
              Application application = (Application) context.getApplicationContext();
              ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
      
              application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
          }
      
          private final Application.ActivityLifecycleCallbacks lifecycleCallbacks = new ActivityLifecycleCallbacksAdapter() {
      
              @Override
              public void onActivityDestroyed(Activity activity) {
                  // 在Activity执行完onDestroyed方法时,调用RefWatcher的watch来监控该Activity是否泄露
                  refWatcher.watch(activity);
              }
          };
          // ...
      }
      
      检查引用
      public final class RefWatcher {
      
          public static final RefWatcher DISABLED = new RefWatcherBuilder<>().build();
      
          // 线程控制器,在 onDestroy() 之后并且主线程空闲时执行内存泄漏检测
          private final WatchExecutor watchExecutor;
          // 判断是否处于调试模式,调试模式中不会进行内存泄漏检测,因为在调试过程中可能会保留上一个引用从而导致错误信息上报。
          private final DebuggerControl debuggerControl;
          // 用于主动触发GC操作
          private final GcTrigger gcTrigger;
          // 堆信息转储者,dump 内存泄漏处的 heap 信息到 hprof 文件
          private final HeapDumper heapDumper;
          private final HeapDump.Listener heapdumpListener;
          private final HeapDump.Builder heapDumpBuilder;
          // 保存每个被检测对象所对应的唯一key
          private final Set retainedKeys;
          // 引用队列,和WeakReference配合使用,当弱引用所引用的对象被GC回收,该弱引用就会被加入到这个队列
          private final ReferenceQueue queue;
      
          public void watch(Object watchedReference) {
              watch(watchedReference, "");
          }
      
          public void watch(Object watchedReference, String referenceName) {
              if (this == DISABLED) {
                  return;
              }
              checkNotNull(watchedReference, "watchedReference");
              checkNotNull(referenceName, "referenceName");
              final long watchStartNanoTime = System.nanoTime();
              // 为被检测对象生成唯一的key值,并保存到retainedKeys
              String key = UUID.randomUUID().toString();
              retainedKeys.add(key);
              // 创建被检测对象的弱引用,并传入该对象的key
              final KeyedWeakReference reference = new KeyedWeakReference(watchedReference, key, referenceName, queue);
      
              // 异步检测这个对象是否被回收
              ensureGoneAsync(watchStartNanoTime, reference);
          }
      
      
          private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
              watchExecutor.execute(new Retryable() {
                  @Override public Retryable.Result run() {
                      return ensureGone(reference, watchStartNanoTime);
                  }
              });
          }
      
          @SuppressWarnings("ReferenceEquality") // Explicitly checking for named null.
          Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
              long gcStartNanoTime = System.nanoTime();
              long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
      
              // 移除对象已经被回收的弱引用
              removeWeaklyReachableReferences();
      
              // 调试模式检测不准确
              if (debuggerControl.isDebuggerAttached()) {
                  // The debugger can create false leaks.
                  return RETRY;
              }
              // 判断引用是否存在,不存在,表示被对象被回收
              if (gone(reference)) {
                  return DONE;
              }
              // 触发GC
              gcTrigger.runGc();
              // GC后再移除对象已经被回收的弱引用
              removeWeaklyReachableReferences();
              // 如果该引用还存在,就表示对象已经泄露
              if (!gone(reference)) {
                  long startDumpHeap = System.nanoTime();
                  long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
      
                  // dump出heap的内存快照
                  File heapDumpFile = heapDumper.dumpHeap();
                  if (heapDumpFile == RETRY_LATER) {
                      // Could not dump the heap.
                      return RETRY;
                  }
                  long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
                  // 构建HeapDump对象
                  HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
                                                     .referenceName(reference.name)
                                                     .watchDurationMs(watchDurationMs)
                                                     .gcDurationMs(gcDurationMs)
                                                     .heapDumpDurationMs(heapDumpDurationMs)
                                                     .build();
                  // 分析HeapDump对象
                  heapdumpListener.analyze(heapDump);
              }
              return DONE;
          }
      
          private boolean gone(KeyedWeakReference reference) {
              return !retainedKeys.contains(reference.key);
          }
      
          private void removeWeaklyReachableReferences() {
              KeyedWeakReference ref;
              // 当弱引用所引用的对象被回收,就会把该引用放到queue中,所以可以通过queue来判断对象是否被回收
              while ((ref = (KeyedWeakReference) queue.poll()) != null) {
                  retainedKeys.remove(ref.key);
              }
          }
          // ...
      }
       
       
      Dump Heap
      AndroidHeapDumper是HeapDumper的实现类。
      public final class AndroidHeapDumper implements HeapDumper {
          
          @Override @Nullable
          public File dumpHeap() {
              File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
      
              if (heapDumpFile == RETRY_LATER) {
                  return RETRY_LATER;
              }
              // ...
              try {
                  // 生成.hprof文件
                  Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
                  cancelToast(toast);
                  notificationManager.cancel(notificationId);
                  return heapDumpFile;
              } catch (Exception e) {
                  CanaryLog.d(e, "Could not dump heap");
                  // Abort heap dump
                  return RETRY_LATER;
              }
          }
      
          // ...
      }
      
      解析hprof
      public final class ServiceHeapDumpListener implements HeapDump.Listener {
          // ...
          @Override 
          public void analyze(@NonNull HeapDump heapDump) {
              checkNotNull(heapDump, "heapDump");
              // 启动HeapAnalyzerServiceService来分析heapDump
              HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
          }
      }
      public final class HeapAnalyzerService extends ForegroundService implements AnalyzerProgressListener {
          // ...
      
          public static void runAnalysis(Context context, HeapDump heapDump,  Class listenerServiceClass) {
              setEnabledBlocking(context, HeapAnalyzerService.class, true);
              setEnabledBlocking(context, listenerServiceClass, true);
              Intent intent = new Intent(context, HeapAnalyzerService.class);
              intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());
              intent.putExtra(HEAPDUMP_EXTRA, heapDump);
              ContextCompat.startForegroundService(context, intent);
          }
      
          @Override 
          protected void onHandleIntentInForeground(@Nullable Intent intent) {
              if (intent == null) {
                  CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");
                  return;
              }
              String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
              HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
      
              HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
      
              // 分析内存泄露的地方
              AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey, heapDump.computeRetainedHeapSize);
              // 发送内存泄露检测结果的通知
              AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
          }
      }
      public final class HeapAnalyzer {
          // ...
      
          public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile, @NonNull String referenceKey, boolean computeRetainedSize) {
              long analysisStartNanoTime = System.nanoTime();
      
              if (!heapDumpFile.exists()) {
                  Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
                  return failure(exception, since(analysisStartNanoTime));
              }
      
              try {
                  listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
                  // 使用haha库解析.hprof文件
                  HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
                  HprofParser parser = new HprofParser(buffer);
                  listener.onProgressUpdate(PARSING_HEAP_DUMP);
                  // 解析.hprof文件生成对应的快照对象
                  Snapshot snapshot = parser.parse();
                  listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
                  // 删除gcRoots中重复的根对象RootObj
                  deduplicateGcRoots(snapshot);
                  listener.onProgressUpdate(FINDING_LEAKING_REF);
                  // 检查对象是否泄露
                  Instance leakingRef = findLeakingReference(referenceKey, snapshot);
      
                  // leakingRef为空表示对象没有泄露
                  if (leakingRef == null) {
                      String className = leakingRef.getClassObj().getClassName();
                      return noLeak(className, since(analysisStartNanoTime));
                  }
                  // 查找引用链
                  return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
              } catch (Throwable e) {
                  return failure(e, since(analysisStartNanoTime));
              }
          }
      }
      
      定位泄露的引用
      public final class HeapAnalyzer {
          // ...
      
          private Instance findLeakingReference(String key, Snapshot snapshot) {
              ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());
              if (refClass == null) {
                  throw new IllegalStateException(
                          "Could not find the " + KeyedWeakReference.class.getName() + " class in the heap dump.");
              }
              List keysFound = new ArrayList<>();
              for (Instance instance : refClass.getInstancesList()) {
                  List values = classInstanceValues(instance);
                  Object keyFieldValue = fieldValue(values, "key");
                  if (keyFieldValue == null) {
                      keysFound.add(null);
                      continue;
                  }
                  String keyCandidate = asString(keyFieldValue);
                  if (keyCandidate.equals(key)) {
                      return fieldValue(values, "referent");
                  }
                  keysFound.add(keyCandidate);
              }
              throw new IllegalStateException("Could not find weak reference with key " + key + " in " + keysFound);
          }
      }
      
      建立引用链
      public final class HeapAnalyzer {
          // ...
      
          private AnalysisResult findLeakTrace(long analysisStartNanoTime, Snapshot snapshot, Instance leakingRef, boolean computeRetainedSize) {
      
              listener.onProgressUpdate(FINDING_SHORTEST_PATH);
              // 查找到GC Roots的最短引用路径
              ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs);
              ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);
      
              String className = leakingRef.getClassObj().getClassName();
      
              // False alarm, no strong reference path to GC Roots.
              if (result.leakingNode == null) {
                  return noLeak(className, since(analysisStartNanoTime));
              }
      
              listener.onProgressUpdate(BUILDING_LEAK_TRACE);
              // 构建泄露的引用链
              LeakTrace leakTrace = buildLeakTrace(result.leakingNode);
      
              long retainedSize;
              if (computeRetainedSize) {
      
                  listener.onProgressUpdate(COMPUTING_DOMINATORS);
                  // 计算内存泄露的大小
                  snapshot.computeDominators();
      
                  Instance leakingInstance = result.leakingNode.instance;
      
                  retainedSize = leakingInstance.getTotalRetainedSize();
      
                  // TODO: check O sources and see what happened to android.graphics.Bitmap.mBuffer
                  if (SDK_INT <= N_MR1) {
                      listener.onProgressUpdate(COMPUTING_BITMAP_SIZE);
                      retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance);
                  }
              } else {
                  retainedSize = AnalysisResult.RETAINED_HEAP_SKIPPED;
              }
      
              return leakDetected(result.excludingKnownLeaks, className, leakTrace, retainedSize, since(analysisStartNanoTime));
          }
      
      }
      
      展示分析结果
      public class DisplayLeakService extends AbstractAnalysisResultService {
          // ...
      
          @Override
          protected final void onHeapAnalyzed(@NonNull AnalyzedHeap analyzedHeap) {
              HeapDump heapDump = analyzedHeap.heapDump;
              AnalysisResult result = analyzedHeap.result;
      
              String leakInfo = leakInfo(this, heapDump, result, true);
              CanaryLog.d("%s", leakInfo);
      
              heapDump = renameHeapdump(heapDump);
              boolean resultSaved = saveResult(heapDump, result);
      
              String contentTitle;
              if (resultSaved) {
                  PendingIntent pendingIntent = DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
                  if (result.failure != null) {
                      contentTitle = getString(R.string.leak_canary_analysis_failed);
                  } else {
                      String className = classSimpleName(result.className);
                      // ...
                  }
                  String contentText = getString(R.string.leak_canary_notification_message);
                  showNotification(pendingIntent, contentTitle, contentText);
              } else {
                  onAnalysisResultFailure(getString(R.string.leak_canary_could_not_save_text));
              }
      
              afterDefaultHandling(heapDump, result, leakInfo);
          }
      
          @Override 
          protected final void onAnalysisResultFailure(String failureMessage) {
              super.onAnalysisResultFailure(failureMessage);
              String failureTitle = getString(R.string.leak_canary_result_failure_title);
              showNotification(null, failureTitle, failureMessage);
          }
      }
      
      总结
      img
      版本二

      https://jsonchao.github.io/2019/01/06/Android%E4%B8%BB%E6%B5%81%E4%B8%89%E6%96%B9%E5%BA%93%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%EF%BC%88%E5%85%AD%E3%80%81%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Leakcanary%E6%BA%90%E7%A0%81%EF%BC%89/

      1. LeakCanary#install()
        public static @NonNull RefWatcher install(@NonNull Application application) {
          return refWatcher(application)// 创建AndroidRefWatcherBuilder对象
              .listenerServiceClass(DisplayLeakService.class)// 配置监听分析结果的服务
              .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())// 配置排除的系统泄露
              .buildAndInstall();// 创建一个Refwatcher并监听Activity的引用
        }
        

        在install()方法中,可以理解为如下4步骤

        1. refWatcher(application)
        2. 链式调用listenerServiceClass(DisplayLeakService.class)
        3. 链式调用excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
        4. 链式调用buildAndInstall()

        首先,我们来看下第一步,这里调用了LeakCanary类的refWatcher方法,如下所示:

        public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
          return new AndroidRefWatcherBuilder(context);
        }
        

        然后新建了一个AndroidRefWatcherBuilder对象,再看看AndroidRefWatcherBuilder这个类。

      2. AndroidRefWatcherBuilder
        /** A {@link RefWatcherBuilder} with appropriate Android defaults. */
        public final class AndroidRefWatcherBuilder extends RefWatcherBuilder {
        ...
            AndroidRefWatcherBuilder(@NonNull Context context) {
                this.context = context.getApplicationContext();
            }
        ...
        }
        

        在AndroidRefWatcherBuilder的构造方法中仅仅是将外部传入的applicationContext对象保存起来了。AndroidRefWatcherBuilder是一个适配Android平台的引用观察者构造器对象,它继承了RefWatcherBuilder,RefWatcherBuilder是一个负责建立引用观察者RefWatcher实例的基类构造器。继续看看RefWatcherBuilder这个类。

      3. RefWatchBuilder
        public class RefWatcherBuilder> {
            ...
            public RefWatcherBuilder() {
                heapDumpBuilder = new HeapDump.Builder();
            }
            ...
        }
        

        在RefWatcher的基类构造器RefWatcherBuilder的构造方法中新建了一个HeapDump的构造器对象。其中HeapDump就是一个保存heap dump信息的数据结构。

        接着来分析下install()方法中的链式调用的listenerServiceClass(DisplayLeakService.class)这部分逻辑。

      4. AndroidRefWatcherBuilder#listenerServiceClass()
        public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
          @NonNull Class listenerServiceClass) {
            return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
        }
        

        在这里,传入了一个DisplayLeakService的Class对象,它的作用是展示泄露分析的结果日志,然后会展示一个用于跳转到显示泄露界面DisplayLeakActivity的通知。在listenerServiceClass()这个方法中新建了一个ServiceHeapDumpListener对象,看看它内部的操作。

      5. ServiceHeapDumpListener
        public final class ServiceHeapDumpListener implements HeapDump.Listener {
            ...
            public ServiceHeapDumpListener(@NonNull final Context context,
                @NonNull final Class listenerServiceClass) {
              this.listenerServiceClass = checkNotNull(listenerServiceClass, "listenerServiceClass");
              this.context = checkNotNull(context, "context").getApplicationContext();
            }
            ...
        }
        

        可以看到这里仅仅是在ServiceHeapDumpListener中保存了DisplayLeakService的Class对象和application对象。它的作用就是接收一个heap dump去分析。

        然后我们继续看install()方法链式调用.excludedRefs(AndroidExcludedRefs.createAppDefaults().build())的这部分代码。先看AndroidExcludedRefs.createAppDefaults()。

      6. AndroidExcludedRefs#createAppDefaults()
        public enum AndroidExcludedRefs {
            ...
            public static @NonNull ExcludedRefs.Builder createAppDefaults() {
              return createBuilder(EnumSet.allOf(AndroidExcludedRefs.class));
            }
            public static @NonNull ExcludedRefs.Builder createBuilder(EnumSet refs) {
              ExcludedRefs.Builder excluded = ExcludedRefs.builder();
              for (AndroidExcludedRefs ref : refs) {
                if (ref.applies) {
                  ref.add(excluded);
                  ((ExcludedRefs.BuilderWithParams) excluded).named(ref.name());
                }
              }
              return excluded;
            }
            ...
        }
        

        先来说下AndroidExcludedRefs这个类,它是一个enum类,它声明了Android SDK和厂商定制的SDK中存在的内存泄露的case,根据AndroidExcludedRefs这个类的类名就可看出这些case都会被Leakcanary的监测过滤掉。目前这个版本是有46种这样的case被包含在内,后续可能会一直增加。然后EnumSet.allOf(AndroidExcludedRefs.class)这个方法将会返回一个包含AndroidExcludedRefs元素类型的EnumSet。Enum是一个抽象类,在这里具体的实现类是通用正规型的RegularEnumSet,如果Enum里面的元素个数大于64,则会使用存储大数据量的JumboEnumSet。最后,在createBuilder这个方法里面构建了一个排除引用的建造器excluded,将各式各样的case分门别类地保存起来再返回出去。

        最后,我们看到链式调用的最后一步buildAndInstall()。

      7. AndroidRefWatcherBuilder#buildAndInstall()
        private boolean watchActivities = true;
        private boolean watchFragments = true;
        
        public @NonNull RefWatcher buildAndInstall() {
            // 1
            if (LeakCanaryInternals.installedRefWatcher != null) {
              throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
            }
        
            // 2 创建RefWatcher对象
            RefWatcher refWatcher = build();
            if (refWatcher != DISABLED) {
              // 3
              LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
              if (watchActivities) {
                // 4 监听Activity的引用
                ActivityRefWatcher.install(context, refWatcher);
              }
              if (watchFragments) {
                // 5 监听Fragment的引用
                FragmentRefWatcher.Helper.install(context, refWatcher);
              }
            }
            // 6
            LeakCanaryInternals.installedRefWatcher = refWatcher;
            return refWatcher;
        }
        

        首先,在注释1处,会判断LeakCanaryInternals.installedRefWatcher是否已经被赋值,如果被赋值了,则会抛出异常,警告buildAndInstall()这个方法应该仅仅只调用一次,在此方法结束时,即在注释6处,该LeakCanaryInternals.installedRefWatcher才会被赋值。再来看注释2处,调用了AndroidRefWatcherBuilder其基类RefWatcherBuilder的build()方法,我们它是如何建造的。

      8. RefWatcherBuilder#build()
        public final RefWatcher build() {
            if (isDisabled()) {
              return RefWatcher.DISABLED;
            }
        
            if (heapDumpBuilder.excludedRefs == null) {
              heapDumpBuilder.excludedRefs(defaultExcludedRefs());
            }
        
            HeapDump.Listener heapDumpListener = this.heapDumpListener;
            if (heapDumpListener == null) {
              heapDumpListener = defaultHeapDumpListener();
            }
        
            DebuggerControl debuggerControl = this.debuggerControl;
            if (debuggerControl == null) {
              debuggerControl = defaultDebuggerControl();
            }
        
            HeapDumper heapDumper = this.heapDumper;
            if (heapDumper == null) {
              heapDumper = defaultHeapDumper();
            }
        
            WatchExecutor watchExecutor = this.watchExecutor;
            if (watchExecutor == null) {
              watchExecutor = defaultWatchExecutor();
            }
        
            GcTrigger gcTrigger = this.gcTrigger;
            if (gcTrigger == null) {
              gcTrigger = defaultGcTrigger();
            }
        
            if (heapDumpBuilder.reachabilityInspectorClasses == null) {
              heapDumpBuilder.reachabilityInspectorClasses(defa  ultReachabilityInspectorClasses());
            }
        
            return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
                heapDumpBuilder);
        }
        

        可以看到,RefWatcherBuilder包含了7个组成部分

        • 1、excludedRefs : 记录可以被忽略的泄漏路径。
        • 2、heapDumpListener : 转储堆信息到hprof文件,并在解析完 hprof 文件后进行回调,最后通知 DisplayLeakService 弹出泄漏提醒。
        • 3、debuggerControl : 判断是否处于调试模式,调试模式中不会进行内存泄漏检测。为什么呢?因为在调试过程中可能会保留上一个引用从而导致错误信息上报
        • 4、heapDumper : 堆信息转储者,dump 内存泄漏处的 heap 信息到 hprof 文件
        • 5、watchExecutor : 线程控制器,在 onDestroy() 之后并且主线程空闲时执行内存泄漏检测
        • 6、gcTrigger : 用于 GC,watchExecutor 首次检测到可能的内存泄漏,会主动进行 GC,GC 之后会再检测一次,仍然泄漏的判定为内存泄漏,最后根据heapDump信息生成相应的泄漏引用链。
        • 7、reachabilityInspectorClasses : 用于要进行可达性检测的类列表。

        最后,会使用建造者模式将这些组成部分构建成一个新的RefWatcher并将其返回。

        我们继续看回到AndroidRefWatcherBuilder的注释3处的 LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true)这行代码。

      9. LeakCanaryInternals#setEnabledAsync()
        public static void setEnabledAsync(Context context, final Class componentClass,
        final boolean enabled) {
          final Context appContext = context.getApplicationContext();
          AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
            @Override public void run() {
              setEnabledBlocking(appContext, componentClass, enabled);
            }
          });
        }
        

        在这里使用了AsyncTask内部自带的THREAD_POOL_EXECUTOR线程池进行阻塞式地显示DisplayLeakActivity。

        然后我们再继续看AndroidRefWatcherBuilder的注释4处的代码。

      10. ActivityRefWatcher#install()
        public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
            Application application = (Application) context.getApplicationContext();
            // 1
            ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
        
            // 2
            application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
        }
        

        可以看到,在注释1处创建一个自己的activityRefWatcher实例,并在注释2处调用了application的registerActivityLifecycleCallbacks()方法,这样就能够监听activity对应的生命周期事件了。继续看看activityRefWatcher.lifecycleCallbacks里面的操作。

        private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
            new ActivityLifecycleCallbacksAdapter() {
              @Override public void onActivityDestroyed(Activity activity) {
                  refWatcher.watch(activity);
              }
        };
        
        public abstract class ActivityLifecycleCallbacksAdapter implements Application.ActivityLifecycleCallbacks {
        
        }
        

        很明显,实现并重写了Application的ActivityLifecycleCallbacks的onActivityDestroyed()方法,这样便能在所有Activity执行完onDestroyed()方法之后调用 refWatcher.watch(activity)这行代码进行内存泄漏的检测了。

        我们再看到注释5处的FragmentRefWatcher.Helper.install(context, refWatcher)这行代码。

      11. FragmentRefWatcher.Helper#install()
        public interface FragmentRefWatcher {
            void watchFragments(Activity activity);
            final class Helper {
              private static final String SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME =
                  "com.squareup.leakcanary.internal.SupportFragmentRefWatcher";
              public static void install(Context context, RefWatcher refWatcher) {
                List fragmentRefWatchers = new ArrayList<>();
                // 1
                if (SDK_INT >= O) {
                  fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
                }
                // 2
                try {
                  Class fragmentRefWatcherClass = Class.forName(SUPPORT_FRAGMENT_REF_WATCHER_CLASS_NAME);
                  Constructor constructor =
                      fragmentRefWatcherClass.getDeclaredConstructor(RefWatcher.class);
                  FragmentRefWatcher supportFragmentRefWatcher   =
                      (FragmentRefWatcher) constructor.newInstance(refWatcher);
                  fragmentRefWatchers.add(supportFragmentRefWatcher);
                } catch (Exception ignored) {
                }
                if (fragmentRefWatchers.size() == 0) {
                  return;
                }
                Helper helper = new Helper(fragmentRefWatchers);
                // 3
                Application application = (Application) context.getApplicationContext();
                application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
              }
            ...
        }
        

        这里面的逻辑很简单,首先在注释1处将Android标准的Fragment的RefWatcher类,即AndroidOFragmentRefWatcher添加到新创建的fragmentRefWatchers中。在注释2处使用反射将leakcanary-support-fragment包下面的SupportFragmentRefWatcher添加进来,如果你在app的build.gradle下没有添加下面这行引用的话,则会拿不到此类,即LeakCanary只会检测Activity和标准Fragment这两种情况。

        debugImplementation   'com.squareup.leakcanary:leakcanary-support-fragment:1.6.2'
        

        继续看到注释3处helper.activityLifecycleCallbacks里面的代码。

        private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
            new ActivityLifecycleCallbacksAdapter() {
              @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                for (FragmentRefWatcher watcher : fragmentRefWatchers) {
                    watcher.watchFragments(activity);
                }
            }
        };
        

        可以看到,在Activity执行完onActivityCreated()方法之后,会调用指定watcher的watchFragments()方法,注意,这里的watcher可能有两种,但不管是哪一种,都会使用当前传入的activity获取到对应的FragmentManager/SupportFragmentManager对象,调用它的registerFragmentLifecycleCallbacks()方法,在对应的onDestroyView()和onDestoryed()方法执行完后,分别使用refWatcher.watch(view)和refWatcher.watch(fragment)进行内存泄漏的检测,代码如下所示。

        @Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
            View view = fragment.getView();
            if (view != null) {
                refWatcher.watch(view);
            }
        }
        
        @Override
        public void onFragmentDestroyed(FragmentManagerfm, Fragment fragment) {
            refWatcher.watch(fragment);
        }
        

        注意,下面到真正关键的地方了,接下来分析refWatcher.watch()这行代码。

      12. RefWatcher#watch()
        public void watch(Object watchedReference, String referenceName) {
            if (this == DISABLED) {
              return;
            }
            checkNotNull(watchedReference, "watchedReference");
            checkNotNull(referenceName, "referenceName");
            final long watchStartNanoTime = System.nanoTime();
            // 1
            String key = UUID.randomUUID().toString();
            // 2
            retainedKeys.add(key);
            // 3
            final KeyedWeakReference reference =
                new KeyedWeakReference(watchedReference, key, referenceName, queue);
            // 4
            ensureGoneAsync(watchStartNanoTime, reference);
        }
        

        注意到在注释1处使用随机的UUID保证了每个检测对象对应的
        key 的唯一性。在注释2处将生成的key添加到类型为CopyOnWriteArraySet的Set集合中。在注释3处新建了一个自定义的弱引用KeyedWeakReference,看看它内部的实现。

      13. KeyedWeakReference
        final class KeyedWeakReference extends WeakReference {
            public final String key;
            public final String name;
        
            KeyedWeakReference(Object referent, String key, String name,
                ReferenceQueue referenceQueue) {
              // 1
              super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));
              this.key = checkNotNull(key, "key");
              this.name = checkNotNull(name, "name");
            }
        }
         

        可以看到,在KeyedWeakReference内部,使用了key和name标识了一个被检测的WeakReference对象。在注释1处,将弱引用和引用队列 ReferenceQueue 关联起来,如果弱引用referent持有的对象被GC回收,JVM就会把这个弱引用加入到与之关联的引用队列referenceQueue中。即 KeyedWeakReference 持有的 Activity 对象如果被GC回收,该对象就会加入到引用队列 referenceQueue 中。

        接着我们回到RefWatcher.watch()里注释4处的ensureGoneAsync()方法。

      14. RefWatcher#ensureGoneAsync()
        private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
            // 1
            watchExecutor.execute(new Retryable() {
                @Override public Retryable.Result run() {
                    // 2
                    return ensureGone(reference watchStartNanoTime);
                }
            });
        }
        

        在ensureGoneAsync()方法中,在注释1处使用 watchExecutor 执行了注释2处的 ensureGone 方法,watchExecutor 是 AndroidWatchExecutor 的实例。

        下面看看watchExecutor内部的逻辑。

      15. AndroidWatchExecutor
        public final class AndroidWatchExecutor implements WatchExecutor {
            ...
            public AndroidWatchExecutor(long initialDelayMillis)     {
              mainHandler = new Handler(Looper.getMainLooper());
              HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
              handlerThread.start();
              // 1
              backgroundHandler = new Handler(handlerThread.getLooper());
              this.initialDelayMillis = initialDelayMillis;
              maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
            }
        
            @Override public void execute(@NonNull Retryable retryable) {
              // 2
              if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
                waitForIdle(retryable, 0);
              } else {
                postWaitForIdle(retryable, 0);
              }
            }
            ...
        }
        

        在注释1处AndroidWatchExecutor的构造方法中,注意到这里使用HandlerThread的looper新建了一个backgroundHandler,后面会用到。在注释2处,会判断当前线程是否是主线程,如果是,则直接调用waitForIdle()方法,如果不是,则调用postWaitForIdle(),来看看这个方法。

        private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
          mainHandler.post(new Runnable() {
            @Override public void run() {
              waitForIdle(retryable, failedAttempts);
            }
          });
        }
        

        很清晰,这里使用了在构造方法中用主线程looper构造的mainHandler进行post,那么waitForIdle()最终也会在主线程执行。接着看看waitForIdle()的实现。

        private void waitForIdle(final Retryable retryable,     final int failedAttempts) {
          Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
            @Override public boolean queueIdle() {
              postToBackgroundWithDelay(retryable, failedAttempts);
              return false;
            }
          });
        }
        

        这里MessageQueue.IdleHandler()回调方法的作用是当 looper 空闲的时候,会回调 queueIdle 方法,然后执行内部的postToBackgroundWithDelay()方法。接下来看看它的实现。

        private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
          long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts),     maxBackoffFactor);
          // 1
          long delayMillis = initialDelayMillis * exponentialBackoffFactor;
          // 2
          backgroundHandler.postDelayed(new Runnable() {
            @Override public void run() {
              // 3
              Retryable.Result result = retryable.run();
              // 4
              if (result == RETRY) {
                postWaitForIdle(retryable, failedAttempts +   1);
              }
            }
          }, delayMillis);
        }
        

        先看到注释4处,可以明白,postToBackgroundWithDelay()是一个递归方法,如果result 一直等于RETRY的话,则会一直执行postWaitForIdle()方法。在回到注释1处,这里initialDelayMillis 的默认值是 5s,因此delayMillis就是5s。在注释2处,使用了在构造方法中用HandlerThread的looper新建的backgroundHandler进行异步延时执行retryable的run()方法。这个run()方法里执行的就是RefWatcher的ensureGoneAsync()方法中注释2处的ensureGone()这行代码,继续看它内部的逻辑。

      16. RefWatcher#ensureGone()
        Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
            long gcStartNanoTime = System.nanoTime();
            long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime -     watchStartNanoTime);
        
            // 1
            removeWeaklyReachableReferences();
        
            // 2
            if (debuggerControl.isDebuggerAttached()) {
              // The debugger can create false leaks.
              return RETRY;
            }
        
            // 3
            if (gone(reference)) {
              return DONE;
            }
        
            // 4
            gcTrigger.runGc();
            removeWeaklyReachableReferences();
        
            // 5
            if (!gone(reference)) {
              long startDumpHeap = System.nanoTime();
              long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
        
              File heapDumpFile = heapDumper.dumpHeap();
              if (heapDumpFile == RETRY_LATER) {
                // Could not dump the heap.
                return RETRY;
              }
        
              long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
        
              HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
                  .referenceName(reference.name)
                  .watchDurationMs(watchDurationMs)
                  .gcDurationMs(gcDurationMs)
                  .heapDumpDurationMs(heapDumpDurationMs)
                  .build();
        
              heapdumpListener.analyze(heapDump);
            }
            return DONE;
        }
        

        在注释1处,执行了removeWeaklyReachableReferences()这个方法,接下来分析下它的含义。

        private void removeWeaklyReachableReferences() {
            KeyedWeakReference ref;
            while ((ref = (KeyedWeakReference) queue.poll()) != null) {
                retainedKeys.remove(ref.key);
            }
        }
        

        这里使用了while循环遍历 ReferenceQueue ,并从 retainedKeys中移除对应的Reference。

        再看到注释2处,当Android设备处于debug状态时,会直接返回RETRY进行延时重试检测的操作。在注释3处,我们看看gone(reference)这个方法的逻辑。

        private boolean gone(KeyedWeakReference reference) {
            return !retainedKeys.contains(reference.key);
        }
        

        这里会判断 retainedKeys 集合中是否还含有 reference,若没有,证明已经被回收了,若含有,可能已经发生内存泄露(或Gc还没有执行回收)。前面的分析中我们知道了 reference 被回收的时候,会被加进 referenceQueue 里面,然后我们会调用removeWeaklyReachableReferences()遍历 referenceQueue 移除掉 retainedKeys 里面的 refrence。

        接着我们看到注释4处,执行了gcTrigger的runGc()方法进行垃圾回收,然后使用了removeWeaklyReachableReferences()方法移除已经被回收的引用。这里我们在深入地分析下runGc()的实现。

        GcTrigger DEFAULT = new GcTrigger() {
            @Override public void runGc() {
              // Code taken from AOSP FinalizationTest:
              // https://android.googlesource.com/platform/libc  ore/+/master/support/src/test/java/libcore/
              // java/lang/ref/FinalizationTester.java
              // System.gc() does not garbage collect every   time. Runtime.gc() is
              // more likely to perform a gc.
              Runtime.getRuntime().gc();
              enqueueReferences();
              System.runFinalization();
            }
        
            private void enqueueReferences() {
              // Hack. We don't have a programmatic way to wait   for the reference queue daemon to move
              // references to the appropriate queues.
              try {
                Thread.sleep(100);
              } catch (InterruptedException e) {
                throw new AssertionError();
              }
            }
        };
        

        这里并没有使用System.gc()方法进行回收,因为system.gc()并不会每次都执行。而是从AOSP中拷贝一段GC回收的代码,从而相比System.gc()更能够保证进行垃圾回收的工作。

        最后我们分析下注释5处的代码处理。首先会判断activity 如果还没有被回收,则证明发生内存泄露,进行if判断里面的操作。在里面先调用堆信息转储者heapDumper的dumpHeap()生成相应的 hprof 文件。这里的heapDumper是一个HeapDumper接口,具体的实现是AndroidHeapDumper。我们分析下AndroidHeapDumper的dumpHeap()方法是如何生成hprof文件的。

        public File dumpHeap() {
            File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
        
            if (heapDumpFile == RETRY_LATER) {
                return RETRY_LATER;
            }
        
            ...
        
            try {
              Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
              ...
        
              return heapDumpFile;
            } catch (Exception e) {
              ...
              // Abort heap dump
              return RETRY_LATER;
            }
        }
        

        这里的核心操作就是调用了
        Android SDK的API Debug.dumpHprofData() 来生成 hprof 文件。

        如果这个文件等于RETRY_LATER则表示生成失败,直接返回RETRY进行延时重试检测的操作。如果不等于的话,则表示生成成功,最后会执行heapdumpListener的analyze()对新创建的HeapDump对象进行泄漏分析。由前面对AndroidRefWatcherBuilder的listenerServiceClass()的分析可知,heapdumpListener的实现
        就是ServiceHeapDumpListener,接着看到ServiceHeapDumpListener的analyze方法。

      17. ServiceHeapDumpListener#analyze()
        @Override public void analyze(@NonNull HeapDump heapDump) {
            checkNotNull(heapDump, "heapDump");
            HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
        }
        

        可以看到,这里执行了HeapAnalyzerService的runAnalysis()方法,为了避免减慢app进程或占用内存,这里将HeapAnalyzerService设置在了一个独立的进程中。接着继续分析runAnalysis()方法里面的处理。

        public final class HeapAnalyzerService extends ForegroundService
        implements AnalyzerProgressListener {
        
            ...
        
            public static void runAnalysis(Context context, HeapDump heapDump,
            Class listenerServiceClass) {
                ...
        
                ContextCompat.startForegroundService(context, intent);
            }
        
            ...
        
            @Override protected void onHandleIntentInForeground(@Nullable Intent intent) {
                ...
        
                // 1
                HeapAnalyzer heapAnalyzer =
                    new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
        
                // 2
                AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
                heapDump.computeRetainedHeapSize);
        
                // 3
                AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
            }
                ...
        }
        

        这里的HeapAnalyzerService实质是一个类型为IntentService的ForegroundService,执行startForegroundService()之后,会回调onHandleIntentInForeground()方法。注释1处,首先会新建一个HeapAnalyzer对象,顾名思义,它就是根据RefWatcher生成的heap dumps信息来分析被怀疑的泄漏是否是真的。在注释2处,然后会调用它的checkForLeak()方法去使用haha库解析 hprof文件,如下所示:

        public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile,
          @NonNull String referenceKey,
          boolean computeRetainedSize) {
            ...
        
            try {
            listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
            // 1
            HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
        
            // 2
            HprofParser parser = new HprofParser(buffer);
            listener.onProgressUpdate(PARSING_HEAP_DUMP);
            Snapshot snapshot = parser.parse();
        
            listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
            // 3
            deduplicateGcRoots(snapshot);
            listener.onProgressUpdate(FINDING_LEAKING_REF);
        
            // 4
            Instance leakingRef = findLeakingReference(referenceKey, snapshot);
        
            // 5
            if (leakingRef == null) {
                return noLeak(since(analysisStartNanoTime));
            }
        
            // 6
            return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
            } catch (Throwable e) {
            return failure(e, since(analysisStartNanoTime));
            }
        }
        

        在注释1处,会新建一个内存映射缓存文件buffer。在注释2处,会使用buffer新建一个HprofParser解析器去解析出对应的引用内存快照文件snapshot。在注释3处,为了减少在Android 6.0版本中重复GCRoots带来的内存压力的影响,使用deduplicateGcRoots()删除了gcRoots中重复的根对象RootObj。在注释4处,调用了findLeakingReference()方法将传入的referenceKey和snapshot对象里面所有类实例的字段值对应的keyCandidate进行比较,如果没有相等的,则表示没有发生内存泄漏,直接调用注释5处的代码返回一个没有泄漏的分析结果AnalysisResult对象。如果找到了相等的,则表示发生了内存泄漏,执行注释6处的代码findLeakTrace()方法返回一个有泄漏分析结果的AnalysisResult对象。

        最后,我们来分析下HeapAnalyzerService中注释3处的AbstractAnalysisResultService.sendResultToListener()方法,很明显,这里AbstractAnalysisResultService的实现类就是我们刚开始分析的用于展示泄漏路径信息得DisplayLeakService对象。在里面直接创建一个由PendingIntent构建的泄漏通知用于供用户点击去展示详细的泄漏界面DisplayLeakActivity。核心代码如下所示:

        public class DisplayLeakService extends AbstractAnalysisResultService {
        
            @Override
            protected final void onHeapAnalyzed(@NonNull AnalyzedHeap analyzedHeap) {
        
                ...
        
                boolean resultSaved = false;
                boolean shouldSaveResult = result.leakFound || result.failure != null;
                if (shouldSaveResult) {
                    heapDump = renameHeapdump(heapDump);
                    // 1
                    resultSaved = saveResult(heapDump, result);
                }
        
                if (!shouldSaveResult) {
                    ...
                    showNotification(null, contentTitle, contentText);
                } else if (resultSaved) {
                    ...
                    // 2
                    PendingIntent pendingIntent =
                        DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
        
                    ...
        
                    showNotification(pendingIntent, contentTitle, contentText);
                } else {
                     onAnalysisResultFailure(getString(R.string.leak_canary_could_not_save_text));
                }
        
            ...
        }
        
        @Override protected final void onAnalysisResultFailure(String failureMessage) {
            super.onAnalysisResultFailure(failureMessage);
            String failureTitle = getString(R.string.leak_canary_result_failure_title);
            showNotification(null, failureTitle, failureMessage);
        }
        

        可以看到,只要当分析的堆信息文件保存成功之后,即在注释1处返回的resultSaved为true时,才会执行注释2处的逻辑,即创建一个供用户点击跳转到DisplayLeakActivity的延时通知。

      18. 你可能感兴趣的:(内存泄漏+leakCanary)