美文网首页
了解LeakCanary1.6.3来龙去脉--源码分析

了解LeakCanary1.6.3来龙去脉--源码分析

作者: JasonChen8888 | 来源:发表于2021-03-23 15:01 被阅读0次

    疑问🤔️

    1. 内存泄露的定义
      内存泄露:指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果
      内存泄漏可以分为4类:常发性内存泄漏;偶发性内存泄漏;一次性内存泄漏;隐式内存泄漏
    2. 内存泄露依据方法
      可达性分析法
    3. 怎么实现内存泄露的分析
      leakCanary

    依赖核心理论-可达性分析法

    可达性分析法 根据是否被GC Root引用确认是否是垃圾对象要被GC回收。
    常见可以作为GC Root的对象有:

    1. 在线程栈中的局部变量(即正在被调用的方法里面的参数和局部变量)
    2. 存活的线程对象
    3. JNI的引用
    4. Class对象(在Android中Class被加载后是不会被卸载的)
    5. 引用类型的静态变量

    LeakCannary的原理分析

    • 通过监听Activity或者Fragment的声明周期,在activity或者fragment结束的时候,监听生命周期转发给 RefWatcher 处理
    1. 顺着代码入口install(application)
      LeakCanary.java
      public static @NonNull RefWatcher install(@NonNull Application application) {
        return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
            .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
            .buildAndInstall();
      }
    
    
      public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
        return new AndroidRefWatcherBuilder(context);
      }
    

    AndroidRefWatcherBuilder.java

    public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
          @NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
        enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass);
        return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
      }
    

    DisplayLeakService类是发生内存泄漏时的通知服务
    excludedRefs()是排除Android源码出现的内存泄漏问题

    1. buildAndInstall()方法的实现
      AndroidRefWatcherBuilder.java中的buildAndInstall()方法,开始进行添加activity和fragment的声明周期的监听
      public @NonNull RefWatcher buildAndInstall() {
        if (LeakCanaryInternals.installedRefWatcher != null) {
          throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
        }
        RefWatcher refWatcher = build();
        //判断是否可以进行监听
        if (refWatcher != DISABLED) {
          if (enableDisplayLeakActivity) {
            // 根据app包名生成LeakCanary关联应用,(桌面上会生成第二个应用图标)
            LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
          }
          //activity监听
          if (watchActivities) {
            ActivityRefWatcher.install(context, refWatcher);
          }
          //fragment的监听
          if (watchFragments) {
            FragmentRefWatcher.Helper.install(context, refWatcher);
          }
        }
        LeakCanaryInternals.installedRefWatcher = refWatcher;
        return refWatcher;
      }
    

    设置关联LeakCanary关联应用
    LeakCanaryInternals.java中的实现:

      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);
          }
        });
      }
    
      public static void setEnabledBlocking(Context appContext, Class<?> componentClass,
          boolean enabled) {
        ComponentName component = new ComponentName(appContext, componentClass);
        PackageManager packageManager = appContext.getPackageManager();
        int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED;
        // Blocks on IPC.
        packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);
      }
    

    ActivityRefWatcher.java类(Activity监听):

      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被销毁的时候,将activity对象通知给RefWatcher(这个作为触发点,leakCanary去进行检测该activity中是否内存泄露)
              refWatcher.watch(activity);
            }
          };
    
    

    FragmentRefWatcher.java接口(fragment监听):
    fragment的监听有个区分:在大于8.0的时候,使用的是新的fragment的监听

     public static void install(Context context, RefWatcher refWatcher) {
          List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();
    
          if (SDK_INT >= O) {
            //android api大于8.0的使用AndroidOFragmentRefWatcher的监听
            fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
          }
    
          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);
    
          Application application = (Application) context.getApplicationContext();
          application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
        }
    
        private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
            new ActivityLifecycleCallbacksAdapter() {
              @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                for (FragmentRefWatcher watcher : fragmentRefWatchers) {
                  watcher.watchFragments(activity);
                }
              }
            };
    
    

    AndroidOFragmentRefWatcher.java(android8.0以上的监听):

      private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
          new FragmentManager.FragmentLifecycleCallbacks() {
    
            @Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
              View view = fragment.getView();
              if (view != null) {
                refWatcher.watch(view);
              }
            }
    
            @Override
            public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
              //在fragment被销毁的时候,将fragment对象通知给RefWatcher(这个作为触发点,leakCanary去进行检测该fragment中是否内存泄露)
              refWatcher.watch(fragment);
            }
          };
    
      @Override public void watchFragments(Activity activity) {
        FragmentManager fragmentManager = activity.getFragmentManager();
        fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
      }
    
    1. 结合上面的接听,在activity或fragment声明周期destroy的阶段,将该对象传给RefWatcher进行watch
      补充点:WeakReference 弱引用的 ReferenceQueue 确认队列中是否有数据,如果有数据,就说明 WeakReference 被GC回收了,如果没有,说明WeakReference的数据无法被GC回收

    RefWatcher.java:

      private final Set<String> retainedKeys;
      private final ReferenceQueue<Object> queue;
    
    
      .............省略部分呢代码..............
    
      //activity或者fragment传入
      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();
        //给每个watchedReference创建一个key,作为跟踪该对象是否被gc
        String key = UUID.randomUUID().toString();
        retainedKeys.add(key);
        // 创建一个KeyedWeakReference类继承WeakReference,gc后,通过确认ReferenceQueue中是否有KeyedWeakReference的数据
        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);
          }
        });
      }
    
      Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
        long gcStartNanoTime = System.nanoTime();
        long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
    
        //将GC掉的对象从内存泄漏的怀疑列表中移除
        removeWeaklyReachableReferences();
    
        if (debuggerControl.isDebuggerAttached()) {
          // The debugger can create false leaks.
          return RETRY;
        }
    
        // 如果列表没有内存泄漏的引用对象,说明当前GC已经回收对象,没有内存泄漏,不需要处理
        if (gone(reference)) {
          return DONE;
        }
    
        // 如果还有内存泄露的怀疑对象 执行一次GC
        gcTrigger.runGc();
    
        //再一次将GC掉的对象从内存泄漏的怀疑列表中移除
        removeWeaklyReachableReferences();
    
        //判断是否还有内存泄露的对象,没有直接返回DONE,有的话,进行dump
        if (!gone(reference)) {
          long startDumpHeap = System.nanoTime();
          long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
          //dump分析,发通知,弹出等待的toast
          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;
      }
    
      //通过key判断是否还有对象无法被回收
      private boolean gone(KeyedWeakReference reference) {
        return !retainedKeys.contains(reference.key);
      }
    
      ////将GC掉的对象从内存泄漏的怀疑名单中移除
      private void removeWeaklyReachableReferences() {
    
        KeyedWeakReference ref;
        while ((ref = (KeyedWeakReference) queue.poll()) != null) {
          retainedKeys.remove(ref.key);
        }
      }
    

    到了这一步,就是可以确认是否有内存泄露了。

    1. dump文件, 发通知
      AndroidHeapDumper.java:
    public File dumpHeap() {
        File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
    
        if (heapDumpFile == RETRY_LATER) {
          return RETRY_LATER;
        }
    
        //等待toast提示
        FutureResult<Toast> waitingForToast = new FutureResult<>();
        showToast(waitingForToast);
    
        if (!waitingForToast.wait(5, SECONDS)) {
          CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
          return RETRY_LATER;
        }
    
        //发通知
        Notification.Builder builder = new Notification.Builder(context)
            .setContentTitle(context.getString(R.string.leak_canary_notification_dumping));
        Notification notification = LeakCanaryInternals.buildNotification(context, builder);
        NotificationManager notificationManager =
            (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        int notificationId = (int) SystemClock.uptimeMillis();
        notificationManager.notify(notificationId, notification);
    
        Toast toast = waitingForToast.get();
        try {
          // 最终使用的是Android提供的工具dump数据到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;
        }
      }
    
    1. 继上面的调用heapdumpListener.analyze(heapDump)进行dump数据分析,启动服务HeapAnalyzerService
      ServiceHeapDumpListener.java
    public final class ServiceHeapDumpListener implements HeapDump.Listener {
    
      private final Context context;
      private final Class<? extends AbstractAnalysisResultService> listenerServiceClass;
    
      public ServiceHeapDumpListener(@NonNull final Context context,
          @NonNull final Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
        this.listenerServiceClass = checkNotNull(listenerServiceClass, "listenerServiceClass");
        this.context = checkNotNull(context, "context").getApplicationContext();
      }
    
      //其实就是启动了一个前台服务在其他进程分析内存泄漏的引用路径
      @Override public void analyze(@NonNull HeapDump heapDump) {
        checkNotNull(heapDump, "heapDump");
        HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
      }
    }
    

    HeapAnalyzerService.java

    public final class HeapAnalyzerService extends ForegroundService
        implements AnalyzerProgressListener {
    
      private static final String LISTENER_CLASS_EXTRA = "listener_class_extra";
      private static final String HEAPDUMP_EXTRA = "heapdump_extra";
    
      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);
      }
    
      public HeapAnalyzerService() {
        super(HeapAnalyzerService.class.getSimpleName(), R.string.leak_canary_notification_analysing);
      }
    
      @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);
      }
    
      @Override public void onProgressUpdate(Step step) {
        int percent = (int) ((100f * step.ordinal()) / Step.values().length);
        CanaryLog.d("Analysis in progress, working on: %s", step.name());
        String lowercase = step.name().replace("_", " ").toLowerCase();
        String message = lowercase.substring(0, 1).toUpperCase() + lowercase.substring(1);
        showForegroundNotification(100, percent, false, message);
      }
    }
    

    HeapAnalyzer.java:

    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);
          HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
          HprofParser parser = new HprofParser(buffer);
          listener.onProgressUpdate(PARSING_HEAP_DUMP);
          Snapshot snapshot = parser.parse();
          listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
          deduplicateGcRoots(snapshot);
          listener.onProgressUpdate(FINDING_LEAKING_REF);
          Instance leakingRef = findLeakingReference(referenceKey, snapshot);
    
          // False alarm, weak reference was cleared in between key check and heap dump.
          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));
        }
      }
    

    总结

    在应用启动时,通过 LeakCanary.install() 监听内存泄漏,LeakCanary的处理过程如下:

    构建 RetWatcher 提供内存泄漏分析前的相关参数(如 DisplayService 通知服务,excludeRefs() 排除系统源码泄漏),通过 Application.registerXxxLifecycleCallback() 监听Activity或Fragment生命周期转发给 RefWatcher

    在Activity或Fragment回调 onDestroy() 时,监听引用对象是否还在 ReferenceQueue 中,有则表示内存泄漏,创建dump文件并通过Android工具 Debug.dumpHprofData() 写入内存泄漏数据,hprof文件将会在另一个前台服务分析

    相关文章

      网友评论

          本文标题:了解LeakCanary1.6.3来龙去脉--源码分析

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