内存泄漏+leakCanary

参考: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的延时通知。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容