内存泄漏+leakCanary

作者: 小杨不想努力了 | 来源:发表于2022-01-20 00:04 被阅读0次

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

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

    内存泄漏:

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

    内存溢出:

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

    常见的内存泄漏

    1. 集合类

      • 原因

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

      • 实例

        //通过 循环申请Object 对象 & 将申请的对象逐个放入到集合List
            List<Object> 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;
        
    2. 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. 非静态内部类/匿名类

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

      常见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<Activity> reference;
                  // 在构造方法中传入需持有的Activity实例
                  public FHandler(Activity activity) {
                      // 使用WeakReference弱引用持有Activity实例
                      reference = new WeakReference<Activity>(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<AndroidRefWatcherBuilder> {
    
        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<String> retainedKeys;
        // 引用队列,和WeakReference配合使用,当弱引用所引用的对象被GC回收,该弱引用就会被加入到这个队列
        private final ReferenceQueue<Object> 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<? extends AbstractAnalysisResultService> 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<String> keysFound = new ArrayList<>();
            for (Instance instance : refClass.getInstancesList()) {
                List<ClassInstance.FieldValue> 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> {
      ...
          AndroidRefWatcherBuilder(@NonNull Context context) {
              this.context = context.getApplicationContext();
          }
      ...
      }
      

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

    3. RefWatchBuilder
      public class RefWatcherBuilder<T extends RefWatcherBuilder<T>> {
          ...
          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<? extends AbstractAnalysisResultService> 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<? extends AbstractAnalysisResultService> 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<AndroidExcludedRefs> 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<FragmentRefWatcher> 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<Object> {
          public final String key;
          public final String name;
      
          KeyedWeakReference(Object referent, String key, String name,
              ReferenceQueue<Object> 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<? extends AbstractAnalysisResultService> 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的延时通知。

    相关文章

      网友评论

        本文标题:内存泄漏+leakCanary

        本文链接:https://www.haomeiwen.com/subject/lharhrtx.html