LeakCanary 源碼分析

LeakCanary是一個(gè)檢測內(nèi)存泄漏的工具,使用非常簡單。主要用來檢測Activity和Fragment內(nèi)存泄漏,如果發(fā)生內(nèi)存泄漏链患,直接在用UI顯示哪里發(fā)生了泄漏并展示對象引用鏈吸占。

LeakCanary地址:https://github.com/square/leakcanary

LeakCanary的使用

在gradle文件中加入依賴

debugImplementation   'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
 // 如果使用了 support fragment晴叨,需要依賴
debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.3'

在Application中加入

public class MyApplication extends Application {
    private RefWatcher refWatcher = null;
    @Override
    public void onCreate() {
        super.onCreate();
        if (!LeakCanary.isInAnalyzerProcess(this)) { // 是否在主進(jìn)程
            refWatcher = LeakCanary.install(this);
        }
    }
}

如果想監(jiān)視其他的Object是否內(nèi)存泄漏了

refWatcher.watch(Object object)

原理

在LeakCanary 的 wiki 就簡述了基本的原理:

  1. RefWatcher.watch() 為監(jiān)視的對象創(chuàng)建一個(gè) KeyedWeakReference;
  2. 然后矾屯,后臺線程會檢查引用是否被清除回收了兼蕊,如果沒有就觸發(fā)GC;
  3. 如果引用還沒被清除件蚕,則堆內(nèi)存dump到文件系統(tǒng)孙技,成一個(gè).hprof文件
  4. HeapAnalyzerService運(yùn)行在另外一個(gè)進(jìn)程,其中內(nèi)部的HeapAnalyzer 使用HAHA庫解析heap dump
  5. 根據(jù)reference key骤坐,HeapAnalyzer 找到對應(yīng)的 KeyedWeakReference绪杏,定位內(nèi)存泄露
  6. HeapAnalyzer 可以找出 GC roots 的最短強(qiáng)引用路徑,并確定是否是泄露纽绍。如果泄漏蕾久,建立導(dǎo)致泄露的引用鏈。
  7. 將泄漏結(jié)果(引用鏈)傳給App進(jìn)程DisplayLeakService拌夏,通知展示

我們主要看的就是LeakCanary.install(this)到底發(fā)生了什么僧著?

在解析流程之前先看下涉及到的幾個(gè)重要的類:

在這里插入圖片描述
  1. RefWatcher: 核心類,負(fù)責(zé)管理和提供入口watch()障簿,由AndroidRefWatcherBuilder創(chuàng)建RefWatcher盹愚,建造者模式

  2. WatchExecutor: 負(fù)責(zé)控制執(zhí)行檢測內(nèi)存泄漏任務(wù)

  3. DebuggerControl:判斷是否處于debug

  4. GcTrigger:負(fù)責(zé)觸發(fā)一次GC

  5. HeapDump:表示指定時(shí)刻的堆棧的快照,AndroidHeapDump為子類

  6. HeapDump.Builder: 負(fù)責(zé)創(chuàng)建HeapDump

  7. HeapDump.Listener:監(jiān)聽器站故,當(dāng)發(fā)生內(nèi)存泄漏的時(shí)候皆怕,會收到消息,需要觸發(fā)分析AndroidHeapDump任務(wù)

  8. ServiceHeapDumpListener:HeapDump.Listener的實(shí)現(xiàn)類西篓,當(dāng)觸發(fā)分析任務(wù)愈腾,調(diào)用HeapAnalyzerService執(zhí)行分析任務(wù)

  9. HeapAnalyzerService:是一個(gè)Android中四大組件之一的Service,運(yùn)行在獨(dú)立的進(jìn)程岂津,負(fù)責(zé)執(zhí)行分析任務(wù)和UI通知

  10. HeapAnalyzer:在HeapAnalyzerService內(nèi)部中虱黄,是對DumpHeap分析內(nèi)存泄漏和找出引用鏈的工具

  11. retainKeys: 是一個(gè)Set<String>,保存著當(dāng)前還沒被回收的Reference的key

  12. ReferenceQueue:引用隊(duì)列吮成,WeakReference可以關(guān)聯(lián)引用隊(duì)列橱乱,當(dāng)reference被回收時(shí),會被加入到ReferenceQueue粱甫,這樣我們就可以判斷哪些對象沒有被回收了

  13. DisplayLeakService:記錄泄漏日志和展示通知的Service

其實(shí)泳叠,leakCanary的基本原理就是利用ReferenceQueue,在Activity銷毀的時(shí)候判斷對象有沒有被加入ReferenceQueue茶宵,若沒有則說明Activity還在存活析二,可能存在泄漏。

GC Root的種類

java 使用引用鏈法來判斷一個(gè)引用是否該被回收,而出發(fā)點(diǎn)就是GC Root

  1. 虛擬機(jī)棧的對象引用
  2. 本地方法棧的對象引用
  3. 方法區(qū)的常量引用
  4. 方法區(qū)的靜態(tài)對象引用

源碼分析

isInAnalyzerProcess()方法是用來判斷當(dāng)前的進(jìn)程是否是在分析進(jìn)程叶摄,因?yàn)槎噙M(jìn)程中,每個(gè)進(jìn)程都會創(chuàng)建Application安拟,而我們需要在非LeakCanary的進(jìn)程中檢測蛤吓。

// LeakCanary
public static boolean isInAnalyzerProcess(@NonNull Context context) {
  Boolean isInAnalyzerProcess = LeakCanaryInternals.isInAnalyzerProcess;
  // This only needs to be computed once per process.
  if (isInAnalyzerProcess == null) {
    isInAnalyzerProcess = isInServiceProcess(context, HeapAnalyzerService.class);
    LeakCanaryInternals.isInAnalyzerProcess = isInAnalyzerProcess;
  }
  return isInAnalyzerProcess;
}

install方法是LeakCanary的總?cè)肟?/p>

public static @NonNull RefWatcher install(@NonNull Application application) {
  return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
      .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
      .buildAndInstall();
}

這里使用了Build建造者模式,利用AndroidRefWatcherBuilder創(chuàng)建RefWatcher

我們分成幾個(gè)部分:

  1. refWatch(application) 創(chuàng)建AndroidRefWatcherBuilder
  2. listenerServiceClass(DisplayLeakService.class) 設(shè)置監(jiān)聽內(nèi)存泄漏和分析結(jié)果的 Service
  3. excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) 設(shè)置忽略的內(nèi)存泄漏的錯(cuò)誤
  4. buildAndInstall() 初始化AndroidRefWatcher
1. refWatch(application)
// LeakCanary
public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
  return new AndroidRefWatcherBuilder(context);
}

這里只是創(chuàng)建了AndroidRefWatcherBuilder糠赦,最后使用Build模式創(chuàng)建AndroidRefWatcher

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

創(chuàng)建了heapDumpListener負(fù)責(zé)heapDump的分析和處理

isAssignableFrom() 是object方法会傲,
例如 a.isAssignableFrom(b) 判斷a是不是b的父類或接口

如果listenerServiceClass繼承或者是DisplayLeakService,則enableDisplayLeakActiviy為true拙泽,表示顯示LeakCanary界面

// ServiceHeapDumpListener 
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);
  }
}

這里簡單看一下ServiceHeapDumpListener 淌山,主要方法在analyze(),實(shí)際上也只是通知了HeapAnalyzerService.runAnalysis()去分析heapDump顾瞻;

HeapAnalyzerService是運(yùn)行在獨(dú)立進(jìn)程的Service泼疑。它是一個(gè)IntentService,執(zhí)行完一次任務(wù)就會終止荷荤。

3. excludedRefs(AndroidExcludedRefs.createAppDefaults().build())

AndroidExcludedRefs.java類中維護(hù)著一系列的特定的內(nèi)存泄漏類型退渗,在執(zhí)行內(nèi)存泄漏和顯示的時(shí)候也會忽略這些類

4. buildAndInstall()
// AndroidRefWatcherBuilder
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) {
      LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
    }
    if (watchActivities) {
      ActivityRefWatcher.install(context, refWatcher);
    }
    if (watchFragments) {
      FragmentRefWatcher.Helper.install(context, refWatcher);
    }
  }
  LeakCanaryInternals.installedRefWatcher = refWatcher;
  return refWatcher;
}

這里也主要分成四個(gè)部分:
(1). build() 構(gòu)建初始化RefWatcher
(2). LeakCancaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true) 設(shè)置DisplayLeakActivity不顯示圖標(biāo)
(3). ActivityRefWatcher.install(context, refWatcher) 監(jiān)測Activity
(4). FragmentRefWatcher.Helper.install(context, refWatcher) 監(jiān)測Fragment

(1) build()
// RefWatcherBuilder
public final RefWatcher build() {
  if (isDisabled()) {
    return RefWatcher.DISABLED;
  }
  ...
  return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,
      heapDumpBuilder);
}

AndroidRefWatcherBuilder 是 RefWatcherBuilder的子類,build()方法內(nèi)部負(fù)責(zé)創(chuàng)建默認(rèn)的組件蕴纳。AndroidRefWatcherBuilder 重寫了部分默認(rèn)方法会油。

// AndroidRefWatcherBuilder
@Override protected @NonNull HeapDumper defaultHeapDumper() {
  LeakDirectoryProvider leakDirectoryProvider =
      LeakCanaryInternals.getLeakDirectoryProvider(context);
  // 創(chuàng)建默認(rèn)的 AndroidHeapDumper
  return new AndroidHeapDumper(context, leakDirectoryProvider);
}

@Override protected @NonNull DebuggerControl defaultDebuggerControl() {
  // 創(chuàng)建默認(rèn)的 AndroidDebuggerControl
  return new AndroidDebuggerControl();
}

@Override protected @NonNull HeapDump.Listener defaultHeapDumpListener() {
  // 創(chuàng)建默認(rèn)的 ServiceHeapDumpListener
  return new ServiceHeapDumpListener(context, DisplayLeakService.class);
}

@Override protected @NonNull WatchExecutor defaultWatchExecutor() {
  // 創(chuàng)建默認(rèn)的 AndroidWatchExecutor
  return new AndroidWatchExecutor(DEFAULT_WATCH_DELAY_MILLIS);
}

@Override protected @NonNull
List<Class<? extends Reachability.Inspector>> defaultReachabilityInspectorClasses() {
  // 創(chuàng)建默認(rèn)的 AndroidReachabilityInspectors
  return AndroidReachabilityInspectors.defaultAndroidInspectors();
}
(2) LeakCancaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true)
// LeakCanaryInternals
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內(nèi)部的并發(fā)線程池 setEnabledBlocking()

// LeakCanaryInternals
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;
  // 設(shè)置是否隱藏應(yīng)用圖標(biāo)
  packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);
}

如果設(shè)置一個(gè)app的mainActivity為COMPONENT_ENABLED_STATE_DISABLED狀態(tài),則不會再launcher的程序圖標(biāo)中發(fā)現(xiàn)該app古毛。

這里設(shè)置了DisplayLeakActivity在Launcher程序圖標(biāo)不出現(xiàn)

(3). ActivityRefWatcher.install(context, refWatcher)
// ActivityRefWatcher
public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
  Application application = (Application) context.getApplicationContext();
  ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
  // 監(jiān)聽application里面的Activity的生命周期
  application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
}

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

這里才是LeakCanary監(jiān)測Activity的核心翻翩,利用ActivityLifecycleCallbacks監(jiān)聽Activity的生命周期,在Activity銷毀的時(shí)候稻薇,onActivityDestroyed調(diào)用refWatcher.watch()

refWatcher.watch()執(zhí)行了實(shí)際的核心工作嫂冻,暫時(shí)先跳過,后續(xù)再繼續(xù)分析refWatcher.watch()

(4). FragmentRefWatcher.Helper.install(context, refWatcher)
// FragmentRefWatcher
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<>();

    if (SDK_INT >= O) { // 如果大于Anroid 26颖低,需要增加AndroidOFragmentRefWatcher
      fragmentRefWatchers.add(new AndroidOFragmentRefWatcher(refWatcher));
    }
    try {
      // 利用反射添加SupportFragmentRefWatcher
      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);
  }

以上部分是添加了FragmentRefWatcher絮吵,用來監(jiān)測Fragment,其中SupportFragmentRefWatcher是在leakcanary-support-fragment包通過反射添加進(jìn)來忱屑,需要在build.gradle添加依賴蹬敲。

// AndroidOFragmentRefWatcher
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) {
        refWatcher.watch(fragment);
      }
    };

@Override public void watchFragments(Activity activity) {
  FragmentManager fragmentManager = activity.getFragmentManager();
  fragmentManager.registerFragmentLifecycleCallbacks(fragmentLifecycleCallbacks, true);
}

監(jiān)測Fragment和監(jiān)測Activity基本是一樣的,利用了FragmentLifecycleCallbacks監(jiān)聽Fragment的生命周期莺戒,在銷毀狀態(tài)onFragmentViewDestroyed和onFragmentDestroyed分別檢測View和Fragment伴嗡。

refWatcher.watch(Object)

無論是檢測Activity還是Fragment,或者是其他類型从铲,都是調(diào)用refWatcher.watch(Object)

再看下AnroidRefWatcher重要的組成部分:

在這里插入圖片描述
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();
  String key = UUID.randomUUID().toString();
  retainedKeys.add(key);//1
  final KeyedWeakReference reference =
      new KeyedWeakReference(watchedReference, key, referenceName, queue);//2
  ensureGoneAsync(watchStartNanoTime, reference);//3
}

watch()主要的工作:

  1. 為檢測的Object創(chuàng)建一個(gè)key瘪校,并添加進(jìn)retainedKeys表示Objectha還存活
  2. 為Object創(chuàng)建一個(gè)弱應(yīng)用,關(guān)聯(lián)queue為引用隊(duì)列
  3. ensureGoneAsync()執(zhí)行監(jiān)測任務(wù)
// RefWatcher
private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {
  watchExecutor.execute(new Retryable() {
     @Override public Retryable.Result run() {
       return ensureGone(reference, watchStartNanoTime);
     }
  });
}

watchExecutor 是AndroidWatchExecutor

//AndroidWatchExecutor
public void execute(@NonNull Retryable retryable) {
  if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
    // 在主線程
    waitForIdle(retryable, 0);
  } else {
    // 切換到主線程
    postWaitForIdle(retryable, 0);
  }
}

private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
  // 利用Handler切換到主線程
  mainHandler.post(new Runnable() {
    @Override public void run() {
      waitForIdle(retryable, failedAttempts);
    }
  });
}

  private void waitForIdle(final Retryable retryable, final int failedAttempts) {
    // 添加任務(wù)到主線程Looper,等待Looper空閑時(shí)候執(zhí)行
    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;
    // backgroudhandler所在的是HandlerThread
    backgroundHandler.postDelayed(new Runnable() {
      @Override public void run() {
        Retryable.Result result = retryable.run();
        if (result == RETRY) {
          postWaitForIdle(retryable, failedAttempts + 1);
        }
      }
    }, delayMillis);
  }

這里利用Looper的IdleHandler阱扬,在Looper空閑的時(shí)候執(zhí)行任務(wù)泣懊。

簡單總結(jié)下AndroidWatcherExecutor,等待主線程空閑的時(shí)候麻惶,在子線程執(zhí)行ensureGone()

// RefWatcher
Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
  long gcStartNanoTime = System.nanoTime();
  long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);

  // 1. 從retainedKeys移除掉已經(jīng)被會回收的弱引用的key
  removeWeaklyReachableReferences();

 // 2. 如果是debug模式馍刮,會繼續(xù)重試
  if (debuggerControl.isDebuggerAttached()) {
    // The debugger can create false leaks.
    return RETRY;
  }

 // 3. 若當(dāng)前引用不在retainedKeys,說明不存在內(nèi)存泄漏
  if (gone(reference)) {
    return DONE;
  }

 // 4. 觸發(fā)一次gc
  gcTrigger.runGc();

 // 5.再次從retainedKeys移除掉已經(jīng)被會回收的弱引用的key
removeWeaklyReachableReferences();

  if (!gone(reference)) {
   // 存在內(nèi)存泄漏
    long startDumpHeap = System.nanoTime();
    long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
   
   // 6. 創(chuàng)建heapDump文件窃蹋,還沒寫入
    File heapDumpFile = heapDumper.dumpHeap();
    if (heapDumpFile == RETRY_LATER) {
      // Could not dump the heap.
      return RETRY;
    }
    long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
    // 7. 創(chuàng)建heapDump
    HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
        .referenceName(reference.name)
        .watchDurationMs(watchDurationMs)
        .gcDurationMs(gcDurationMs)
        .heapDumpDurationMs(heapDumpDurationMs)
        .build();
    // 8.調(diào)用heapdumpListener分析
    heapdumpListener.analyze(heapDump);
  }
  return DONE;
}

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

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

上面的注釋把流程基本闡述清楚了卡啰,基本邏輯是

  1. 利用引用隊(duì)列移除掉已經(jīng)回收的對象
  2. 可能因?yàn)榛厥詹患皶r(shí),所以再次gc
  3. 若對象還沒被回收警没,則發(fā)生內(nèi)存泄漏
  4. 創(chuàng)建HeapDump匈辱,后臺service執(zhí)行分析任務(wù)

最后看HeapAnalyzerService分析HeapDump

// HeapAnalyzerService
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);
}

這里的listenerServiceClass 就是從最上面?zhèn)鬟f過來的DisplayLeakService.class,最后他負(fù)責(zé)記錄日志和展示通知杀迹。

runAnalysis() 啟動(dòng)了HeapAnalyzerService亡脸,它是一個(gè)IntentService運(yùn)行在獨(dú)立進(jìn)程,負(fù)責(zé)分析HeapDump和顯示通知佛南,既然是IntentService梗掰,直接看onHandleIntent(),內(nèi)部又調(diào)用了onHandleIntentInForeground()

protected void onHandleIntentInForeground(@Nullable Intent intent) {
    String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA); // DisplayLeakService.class
    HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
    // 創(chuàng)建HeapAnalyzer
    HeapAnalyzer heapAnalyzer =
        new HeapAnalyzer(heapDump.excludedRefs, this, heapDump.reachabilityInspectorClasses);
    // HeapAnanlyzer工具分析
    AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
        heapDump.computeRetainedHeapSize);
    // 啟動(dòng)DisplayLeakService記錄日志和展示通知
    AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
  }

HeapAnalyzer 可以找出 GC roots 的最短強(qiáng)引用路徑嗅回,并確定是否是泄露及穗。如果泄漏,建立導(dǎo)致泄露的引用鏈绵载。

// 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);
    //將heap文件封裝成MemoryMappedFileBuffer
    HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
    //創(chuàng)建hprof解析器埂陆,解析hprof文件
    HprofParser parser = new HprofParser(buffer);
    listener.onProgressUpdate(PARSING_HEAP_DUMP);
    Snapshot snapshot = parser.parse();
    listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
    // 移除相同GC root
    deduplicateGcRoots(snapshot);
    listener.onProgressUpdate(FINDING_LEAKING_REF);
    // 找出內(nèi)存泄漏對象
    Instance leakingRef = findLeakingReference(referenceKey, snapshot);
     //檢測是否存在泄漏的引用
    if (leakingRef == null) {
      String className = leakingRef.getClassObj().getClassName();
      return noLeak(className, since(analysisStartNanoTime));
    }
     //根據(jù)leakingRef尋找引用路徑
    return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
  } catch (Throwable e) {
    return failure(e, since(analysisStartNanoTime));
  }
}

再往下就是如何分析HeapDump,不是本文的重點(diǎn)娃豹。

總結(jié):

在這里插入圖片描述
  1. 無論你是監(jiān)測Activity還是Fragment亦或是其他Object焚虱,最后都是RefWatcher.watch(object)
  2. RefWatcher添加監(jiān)測的引用, 在主線程 idle 后進(jìn)行一次強(qiáng)制 gc 后再判斷該引用是否在引用隊(duì)列中懂版,否則就可能是內(nèi)存泄漏
  3. RefWatcher 通過 HeapDumper 創(chuàng)建HeapDump 和 hprof 文件
  4. HeapAnalyzerService的HeapAnalyzer 分析內(nèi)存泄漏和找出引用鏈鹃栽,最后DisplayLeakService通知
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市躯畴,隨后出現(xiàn)的幾起案子民鼓,更是在濱河造成了極大的恐慌,老刑警劉巖蓬抄,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丰嘉,死亡現(xiàn)場離奇詭異,居然都是意外死亡嚷缭,警方通過查閱死者的電腦和手機(jī)饮亏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門耍贾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人路幸,你說我怎么就攤上這事荐开。” “怎么了劝赔?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵誓焦,是天一觀的道長。 經(jīng)常有香客問我着帽,道長,這世上最難降的妖魔是什么移层? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任仍翰,我火速辦了婚禮,結(jié)果婚禮上观话,老公的妹妹穿的比我還像新娘予借。我一直安慰自己,他們只是感情好频蛔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布灵迫。 她就那樣靜靜地躺著,像睡著了一般晦溪。 火紅的嫁衣襯著肌膚如雪瀑粥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天三圆,我揣著相機(jī)與錄音狞换,去河邊找鬼。 笑死舟肉,一個(gè)胖子當(dāng)著我的面吹牛修噪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播路媚,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼黄琼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了整慎?” 一聲冷哼從身側(cè)響起脏款,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎院领,沒想到半個(gè)月后弛矛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡比然,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年丈氓,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡万俗,死狀恐怖湾笛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情闰歪,我是刑警寧澤嚎研,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站库倘,受9級特大地震影響临扮,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜教翩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一杆勇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧饱亿,春花似錦蚜退、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至配猫,卻和暖如春幅恋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背章姓。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工佳遣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人凡伊。 一個(gè)月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓零渐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親系忙。 傳聞我的和親對象是個(gè)殘疾皇子诵盼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 一、前言 1.簡介 A small leak will sink a great ship —— Benjamin...
    仰簡閱讀 2,553評論 0 19
  • 轉(zhuǎn)自我的csdn博客银还,轉(zhuǎn)載請注明出處风宁。 Leakcanary是square推出的內(nèi)存泄露分析工具,使用很簡單蛹疯,可謂...
    srp123閱讀 10,915評論 3 8
  • 大家好戒财,我是蒼王。 以下是我這個(gè)系列的相關(guān)文章捺弦,有興趣可以參考一下饮寞,可以給個(gè)喜歡或者關(guān)注我的文章孝扛。 [Androi...
    CangWang閱讀 4,814評論 5 16
  • 居江南冬天苦始,踏雪尋梅“四海山”, “四海山”觀雪,如銀濤雪浪 如入蓬萊仙境慌申,似幻隨影 從山腳到巔峰一山應(yīng)四季 大雪...
    象浦閱讀 1,272評論 12 41
  • 父親打來電話,我和往常一樣掛了電話陌选,又打了過去。父親說:“我不愿意到你哥家過年蹄溉∽捎停”原來好強(qiáng)的老爸不愿意再次...
    lizhizhong1764閱讀 391評論 0 1