LeakCanary源碼解析

開源庫路徑https://github.com/square/leakcanaryandroid

源碼結構

image

  • leakcanary-watcher: 這是一個通用的內存檢測器,對外提供一個 RefWatcher#watch(Object watchedReference),它不只可以檢測Activity,還能監測任意常規的 Java Object 的泄漏狀況。
  • leakcanary-android: 這個 module 是與 Android 的接入點,用來專門監測 Activity 的泄漏狀況,內部使用了 application#registerActivityLifecycleCallbacks 方法來監聽 onDestory 事件,而後利用 leakcanary-watcher 來進行弱引用+手動 GC 機制進行監控。
  • leakcanary-analyzer: 這個 module 提供了 HeapAnalyzer,用來對 dump 出來的內存進行分析並返回內存分析結果AnalysisResult,內部包含了泄漏發生的路徑等信息供開發者尋找定位。
  • leakcanary-android-no-op: 這個 module 是專門給 release 的版本用的,內部只提供了兩個徹底空白的類 LeakCanary 和 RefWatcher,這兩個類不會作任何內存泄漏相關的分析。由於 LeakCanary 自己會因爲不斷 gc 影響到 app 自己的運行,並且主要用於開發階段的內存泄漏檢測。所以對於 release 則能夠 disable 全部泄漏分析。

原理簡介

LeakCanary的原理很是簡單。正常狀況下一個Activity在執行Destroy以後就要銷燬,LeakCanary作的就是在一個Activity/Fragment Destroy以後將它放在一個WeakReference中,而後將這個WeakReference關聯到一個ReferenceQueue,查看ReferenceQueue是否存在Activity的引用,若是不在這個隊列中,執行一些GC清洗操做,再次查看。若是不存在則證實該Activity/Fragment泄漏了,以後Dump出heap信息,並用haha這個開源庫去分析泄漏路徑。git

基本原理圖示

image

源碼分析

簡單分析,只分析如何實現內存泄漏檢測的基本思路github

第一步:入口函數分析LeakCanary.install(Application application)

集成使用LeakCanary基本上是在Application onCreate中調用便可LeakCanary.install(this); ,這是總的入口函數,第一步分析就從這裏開始。bash

/**
   * Creates a {@link RefWatcher} that works out of the box, and starts watching activity
   * references (on ICS+).
   */
  public static @NonNull RefWatcher install(@NonNull Application application) {
    //返回的是AndroidRefWatcherBuilder繼承自RefWatcher對象
    return refWatcher(application).listenerServiceClass(DisplayLeakService.class)  
        .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
        .buildAndInstall();
  }
  
  分析一:返回AndroidRefWatcherBuilder對象
  refWatcher(application)
  
  分析二:調用返回AndroidRefWatcherBuilder.buildAndInstall
  buildAndInstall
複製代碼

分析一: refWatcher(application)

該方法調用的refWatcher返回了AndroidRefWatcherBuilder對象:app

public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
    return new AndroidRefWatcherBuilder(context);
  }
複製代碼

分析二:AndroidRefWatcherBuilder.buildAndInstall

進行設置監控所須要的相關係統lifeCycle回調,包含ActivityLifecycleCallbacks以及FragmentLifecycleCallbacks。dom

/**
   * Creates a {@link RefWatcher} instance and makes it available through {@link
   * LeakCanary#installedRefWatcher()}.
   *
   * Also starts watching activity references if {@link #watchActivities(boolean)} was set to true.
   *
   * @throws UnsupportedOperationException if called more than once per Android process.
   */
  public @NonNull RefWatcher buildAndInstall() {
    //buildAndInstall只容許調用一次
    if (LeakCanaryInternals.installedRefWatcher != null) { 
      throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
    }
    //建立用於實際處理判斷內存泄漏的監控對象RefWatcher,放在內容第二步分析
    RefWatcher refWatcher = build();
    if (refWatcher != DISABLED) {
      LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
      if (watchActivities) {
        //watchActivities默認爲true,開始activity引用的監控
        ActivityRefWatcher.install(context, refWatcher); 
      }
      if (watchFragments) {
        //watchFragments默認爲true,開始fragment引用的監控
        FragmentRefWatcher.Helper.install(context, refWatcher); 
      }
    }
    //賦值installedRefWatcher,用於判斷已建立成功
    LeakCanaryInternals.installedRefWatcher = refWatcher; 
    return refWatcher;
  }
  
  分析三:設置activity資源泄漏監控:
  ActivityRefWatcher.install 
  分析四:設置Fragment資源泄漏監控:
  FragmentRefWatcher.Helper.install
複製代碼

分析三:ActivityRefWatcher.install

這邊主要是對app註冊了個ActivityLifecycleCallbacks,在每次activity被銷燬後都會回調到onActivityDestroyed,在onActivityDestroyed中獲取在理論上即將被銷燬的activity對象,調用refWatcher.watch檢測其是否發生泄漏異步

public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
    Application application = (Application) context.getApplicationContext();
    ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
    //註冊個lifecycleCallbacks,在裏面分析activity的內存泄漏問題
    application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
  }
 
  //app全部activity生命週期結束自動回調
  private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
      new ActivityLifecycleCallbacksAdapter() {
        @Override public void onActivityDestroyed(Activity activity) {
          //調用的仍是refWatcher操做,ActivityRefWatcher只是爲了activity週期監聽
          refWatcher.watch(activity);
        }
      };
      
      下文第二步分析activity是否存在內存泄漏:
      refWatcher.watch(activity)
      
複製代碼

分析四:FragmentRefWatcher.Helper.install

這邊主要用於ActivityLifecycleCallbacks中各個activity建立的時候, 獲取到activity對應的FragmentManager註冊FragmentLifecycleCallbacks.後續當有Fragment消耗觸發onFragmentViewDestroyed或者onFragmentDestroyed時,則獲取理論上即將被銷燬的view/fragment對象,調用refWatcher.watch檢測其是否發生泄漏。ide

public static void install(Context context, RefWatcher refWatcher) {
      List<FragmentRefWatcher> fragmentRefWatchers = new ArrayList<>();

      if (SDK_INT >= O) {
        //添加了個AndroidOFragmentRefWatcher用於對android.app.FragmentManager設置FragmentLifecycleCallbacks
        fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
      }

      try {
        //反射添加SupportFragmentRefWatcher用於對android.support.v4.app.FragmentManager設置FragmentLifecycleCallbacks
        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) {
        ignored.printStackTrace();
      }

      if (fragmentRefWatchers.size() == 0) {
        return;
      }

      Helper helper = new Helper(fragmentRefWatchers);

      //這邊再次註冊了另一個ActivityLifecycleCallbacks
      Application application = (Application) context.getApplicationContext();
      application.registerActivityLifecycleCallbacks(helper.activityLifecycleCallbacks);
    }

    //該ActivityLifecycleCallbacks主要在onActivityCreated回調的時候執行上面添加的FragmentRefWatcher.watchFragments方法
    private final Application.ActivityLifecycleCallbacks activityLifecycleCallbacks =
        new ActivityLifecycleCallbacksAdapter() {
          @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            for (FragmentRefWatcher watcher : fragmentRefWatchers) {
              watcher.watchFragments(activity);
            }
          }
        };
        
    分析五:fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher)):
    用於對android.app.FragmentManager設置FragmentLifecycleCallbacks
    
    分析六:fragmentRefWatchers.add(supportFragmentRefWatcher):
    用於對android.support.v4.app.FragmentManager設置FragmentLifecycleCallbacks
複製代碼

分析五:fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher)):

添加了個AndroidOFragmentRefWatcher用於對android.app.FragmentManager設置FragmentLifecycleCallbacks,後續在fragment生命週期結束時獲取並判斷是否存在fragment內存泄漏。函數

AndroidOFragmentRefWatcher.watchFragments:oop

private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
      new FragmentManager.FragmentLifecycleCallbacks() {

        @Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
          //檢測即將被回收的view是否存在泄漏
          View view = fragment.getView();
          if (view != null) {
            refWatcher.watch(view);
          }
        }

        @Override
        public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
        //檢測即將被回收的fragment是否存在泄漏
          refWatcher.watch(fragment);
        }
      };

  @Override public void watchFragments(Activity activity) {
    FragmentManager fragmentManager = activity.getFragmentManager();
    //對activity註冊FragmentLifecycleCallbacks生命週期監聽
    fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
  }
複製代碼

分析六:fragmentRefWatchers.add(supportFragmentRefWatcher):

添加了個SupportFragmentRefWatcher用於對android.support.v4.app.FragmentManager設置FragmentLifecycleCallbacks,後續在fragment生命週期結束時獲取並判斷是否存在fragment內存泄漏。

SupportFragmentRefWatcher.watchFragments:

private final FragmentManager.FragmentLifecycleCallbacks fragmentLifecycleCallbacks =
      new FragmentManager.FragmentLifecycleCallbacks() {

        @Override public void onFragmentViewDestroyed(FragmentManager fm, Fragment fragment) {
        //檢測即將被回收的view是否存在泄漏
          View view = fragment.getView();
          if (view != null) {
            refWatcher.watch(view);
          }
        }

        @Override public void onFragmentDestroyed(FragmentManager fm, Fragment fragment) {
        //檢測即將被回收的fragment是否存在泄漏
          refWatcher.watch(fragment);
        }
      };

  @Override public void watchFragments(Activity activity) {
    if (activity instanceof FragmentActivity) {
    //對activity註冊FragmentLifecycleCallbacks生命週期監聽
      FragmentManager supportFragmentManager =
          ((FragmentActivity) activity).getSupportFragmentManager();
      supportFragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
    }
  }
複製代碼

第二步:內存是否泄漏判斷refWatcher.watch()

首先從RefWatcher對象的建立開始

/** Creates a {@link RefWatcher}. */
 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();
    }

    //默認爲null
    DebuggerControl debuggerControl = this.debuggerControl;
    if (debuggerControl == null) {
      debuggerControl = defaultDebuggerControl();
    }

    HeapDumper heapDumper = this.heapDumper;
    if (heapDumper == null) {
      heapDumper = defaultHeapDumper();
    }

    //設置默認的監控執行處理器defaultWatchExecutor,調用AndroidRefWatcherBuilder.defaultWatchExecutor()獲取
    WatchExecutor watchExecutor = this.watchExecutor;
    if (watchExecutor == null) {
      watchExecutor = defaultWatchExecutor();
    }
    //獲取Gc處理器RefWatcherBuilder.defaultGcTrigger()
    GcTrigger gcTrigger = this.gcTrigger;
    if (gcTrigger == null) {
      gcTrigger = defaultGcTrigger();
    }

    if (heapDumpBuilder.reachabilityInspectorClasses == null) {
      heapDumpBuilder.reachabilityInspectorClasses(defaultReachabilityInspectorClasses());
    }
   //返回內存泄漏監控處理者RefWatcher
    return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
        heapDumpBuilder);
  }
  
  分析七:defaultWatchExecutor();
  
複製代碼

分析七:defaultWatchExecutor

AndroidWatchExecutor對象的建立:

//默認延時參數5秒
private static final long DEFAULT_WATCH_DELAY_MILLIS = SECONDS.toMillis(5);
  
    @Override protected @NonNull WatchExecutor defaultWatchExecutor() {
    return new AndroidWatchExecutor(DEFAULT_WATCH_DELAY_MILLIS);
  }
複製代碼

AndroidWatchExecutor實現的功能

AndroidWatchExecutor主要是作了一個簡單的延時功能,由於activity、fragment等處罰ondestroy時,這些對象理論上即將被回收,可是還未被回收,因此AndroidWatchExecutor默認將檢測任務發送到異步線程中作了個5秒的延時,注意這邊是在異步線程,不阻塞主線程。在延時時間到了後,將檢測任務再發送回主線程進行檢測,注意這邊之因此再發送回主線程,是由於gc操做只能在主線程觸發。

AndroidWatchExecutor類:
  public final class AndroidWatchExecutor implements WatchExecutor {

  static final String LEAK_CANARY_THREAD_NAME = "LeakCanary-Heap-Dump";
  private final Handler mainHandler;
  private final Handler backgroundHandler;
  private final long initialDelayMillis;
  private final long maxBackoffFactor;

  public AndroidWatchExecutor(long initialDelayMillis) {
    //建立運行與主線程的mainHandler
    mainHandler = new Handler(Looper.getMainLooper());
    HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
    handlerThread.start();
    //建立運行於後臺線程的backgroundHandler
    backgroundHandler = new Handler(handlerThread.getLooper());
    //默認爲5s
    this.initialDelayMillis = initialDelayMillis;
    maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
  }

  @Override public void execute(@NonNull Retryable retryable) {
    if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
      //當前是主線程則執行waitForIdle
      waitForIdle(retryable, 0);
    } else {
      //當前是後臺線程則執行postWaitForIdle
      postWaitForIdle(retryable, 0);
    }
  }

  private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
    //將檢測任務Retryable post到主線程中去執行
    mainHandler.post(new Runnable() {
      @Override public void run() {
        waitForIdle(retryable, failedAttempts);
      }
    });
  }

  private void waitForIdle(final Retryable retryable, final int failedAttempts) {
    // This needs to be called from the main thread.
    //當主線程空閒時則執行postToBackgroundWithDelay
    Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
      @Override public boolean queueIdle() {
        postToBackgroundWithDelay(retryable, failedAttempts);
        return false;
      }
    });
  }

  private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
    long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
    long delayMillis = initialDelayMillis * exponentialBackoffFactor;
    //延時5秒執行Retryable檢測
    backgroundHandler.postDelayed(new Runnable() {
      @Override public void run() {
        Retryable.Result result = retryable.run();
        if (result == RETRY) {
          postWaitForIdle(retryable, failedAttempts + 1);
        }
      }
    }, delayMillis);
  }
}
複製代碼

判斷是否存在內存泄漏調用RefWatcher.watch:

public void watch(Object watchedReference, String referenceName) {
    if (this == DISABLED) {
      return;
    }
    checkNotNull(watchedReference, "watchedReference");
    checkNotNull(referenceName, "referenceName");
    //開始檢測的時間
    final long watchStartNanoTime = System.nanoTime();
    //產生隨機的key , 做爲須要檢測的對象的惟一標識
    String key = UUID.randomUUID().toString();
    //保存該key
    retainedKeys.add(key);
    //建立對應的對須要監控的watchedReference對象的弱引用並與ReferenceQueue綁定
    final KeyedWeakReference reference =
        new KeyedWeakReference(watchedReference, key, referenceName, queue);
   //開始確認該對象是否被回收了
    ensureGoneAsync(watchStartNanoTime, reference);
  }
複製代碼

作了個簡單的線程判斷ensureGoneAsync

這邊看到使用到了上面watchExecutor延時5秒後,再執行ensureGone

private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
    watchExecutor.execute(new Retryable() {
      @Override public Retryable.Result run() {
        return ensureGone(reference, watchStartNanoTime);
      }
    });
  }
複製代碼

確認是否回收ensureGone

該函數執行的一個基本操做就是: 1.首先判斷ReferenceQueue是否存在要檢測內存泄漏的reference對象,不存在則表明可能發生泄漏

2.主動觸發一次gc,進行內存回收

3.再次判斷ReferenceQueue是否存在要檢測內存泄漏的reference對象,不存在則表明可能發生泄漏

4.若發生泄漏則dump出內存hprof文件,進行分析,從中分析出內存泄漏的路徑

Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
    //gc準備開啓的時間
    long gcStartNanoTime = System.nanoTime();
    //開始監控到準備gc的時間,大概5秒多,由於前邊延時5秒
    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
    //移除已經被回收內存的監控對象的Key
    removeWeaklyReachableReferences();

    if (debuggerControl.isDebuggerAttached()) {
      // The debugger can create false leaks.
      return RETRY;
    }
    //判斷該reference對象是否被回收了,若是已經被回收,返回DONE,
    if (gone(reference)) {
      return DONE;
    }
    //若是還沒有被回收,則主動觸發一次gc
    gcTrigger.runGc();
    //移除已經被回收內存的監控對象的Key
    removeWeaklyReachableReferences();
    //判斷該reference對象是否被回收了,若是已經被回收,返回DONE,
    if (!gone(reference)) {
      //該reference對象還沒有被回收
      long startDumpHeap = System.nanoTime();
      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
      //主動dump出內存Hprof文件
      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();
      //將hprof進行分析出泄漏的點並經過ui通知用戶
      heapdumpListener.analyze(heapDump);
    }
    return DONE;
  }
複製代碼

參考

allenwu.itscoder.com/leakcanary-…

公衆號

相關文章
相關標籤/搜索