LeakCanary浅析

一、使用

public class ExampleApplication extends Application {
 public static RefWatcher getRefWatcher(Context context) {
ExampleApplication application = (ExampleApplication) context.getApplicationContext();
return application.refWatcher;
}
private RefWatcher refWatcher;
@Override public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
 //监测当前应用内存泄露的是进程是否正在运行,
 //如果正在运行则不用再次进行初始化,否则进行初始化操作。
  return;
}
refWatcher =LeakCanary.install(this);
// Normal app init code...
    }
}

LeakCanary的使用非常的简单,两行代码搞定。当然也可以主动的添加需要监听的对象;
LeakCanary.install(this);调用之后会返回一个RefWatcher对象,然后就可以调用观察分析某个对象:

public abstract class BaseFragment extends Fragment {
@Override public void onDestroy() {
super.onDestroy();
RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
refWatcher.watch(this);
 }
}

我们就从这上面那两行代码开始进行分析。

二、LeakCanary.isInAnalyzerProcess(this)

public static boolean isInAnalyzerProcess(Context context) {
return isInServiceProcess(context, HeapAnalyzerService.class);
}

->LeakCanary.isInServiceProcess()

 public static boolean isInServiceProcess(Context context, Class<? extends Service> serviceClass) {
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo;
try {
  //获取当前应用注册的Service信息
  packageInfo = packageManager.getPackageInfo(context.getPackageName(), GET_SERVICES);
} catch (Exception e) {
  CanaryLog.d(e, "Could not get package info for %s", context.getPackageName());
  return false;
}
String mainProcess = packageInfo.applicationInfo.processName;

ComponentName component = new ComponentName(context, serviceClass);
ServiceInfo serviceInfo;
try {
  //查找HeapAnalyzerService信息
  serviceInfo = packageManager.getServiceInfo(component, 0);
} catch (PackageManager.NameNotFoundException ignored) {
  // 找不到HeapAnalyzerService相应信息 返回false
  return false;
}
//判断HeapAnalyzerService所在的进程名称是否和宿主进程一样
if (serviceInfo.processName.equals(mainProcess)) {
  return false;
}
//判断HeapAnalyzerService是否正在运行
int myPid = android.os.Process.myPid();
ActivityManager activityManager =
    (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.RunningAppProcessInfo myProcess = null;
List<ActivityManager.RunningAppProcessInfo> runningProcesses =
    activityManager.getRunningAppProcesses();
if (runningProcesses != null) {
  for (ActivityManager.RunningAppProcessInfo process : runningProcesses) {
    if (process.pid == myPid) {
      myProcess = process;
      break;
    }
  }
}
if (myProcess == null) {
  CanaryLog.d("Could not find running process for %d", myPid);
  return false;
}

return myProcess.processName.equals(serviceInfo.processName);
}

这段代码相对比较简单主要是判断HeapAnalyzerService是否已经启动了,如果启动了就不用再进行初始化启动了。

三、LeakCanary.install(this)

初始化并且开始监听每个Activity销毁时是否存在内存泄露。

  public static RefWatcher install(Application application) {
return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
    .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
    .buildAndInstall();
 }
3.1、LeakCanary.refWatcher()
  public static AndroidRefWatcherBuilder refWatcher(Context context) {
return new AndroidRefWatcherBuilder(context);

}

3.2、AndroidRefWatcherBuilder.listenerServiceClass()

添加监听

public AndroidRefWatcherBuilder listenerServiceClass(
  Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
}

创建内存回收监听器
-> ServiceHeapDumpListener()

public ServiceHeapDumpListener(Context context,
  Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
  //向PageManage注册DisplayLeakService和HeapAnalyzerService
setEnabled(context, listenerServiceClass, true);
setEnabled(context, HeapAnalyzerService.class, true);
this.listenerServiceClass = checkNotNull(listenerServiceClass, "listenerServiceClass");
this.context = checkNotNull(context, "context").getApplicationContext();
}
3.3、AndroidRefWatcherBuilder.buildAndInstall()
  public RefWatcher buildAndInstall() {
RefWatcher refWatcher = build();
if (refWatcher != DISABLED) {
    //向PageManage注册DisplayLeakActivity
  LeakCanary.enableDisplayLeakActivity(context);
  //监听activity销毁事件
  ActivityRefWatcher.install((Application) context, refWatcher);
}
return refWatcher;

}

3.4、 ActivityRefWatcher.install()
public static void install(Application application, RefWatcher refWatcher) {
new ActivityRefWatcher(application, refWatcher).watchActivities();
}

注册Activity销毁事件监听
-> ActivityRefWatcher.watchActivities()

 public void watchActivities() {
// Make sure you don't get installed twice.
stopWatchingActivities();
application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}
  private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
  new Application.ActivityLifecycleCallbacks() {
    @Override public void onActivityDestroyed(Activity activity) {
      //在Activity销毁会会调用这个方法
  ActivityRefWatcher.this.onActivityDestroyed(activity);
    }
  };
}

在Activity销毁时,对销毁的Activity的进行追踪和分析。

3.5、RefWatcher.ensureGoneAsync()
  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);
//移除已经被回收的activity实例对应的KeyedWeakReference(见3.5.1)
removeWeaklyReachableReferences();

if (debuggerControl.isDebuggerAttached()) {
  // The debugger can create false leaks.
  return RETRY;
}
//判断当前activity是否被回收了
if (gone(reference)) {
  return DONE;
}
//发起gc
gcTrigger.runGc();
 //移除已经被回收的activity实例对应的KeyedWeakReference(见3.5.1)
removeWeaklyReachableReferences();
//当 retainedKeys中还存在当前activity实例对应的KeyedWeakReference时,
//说明当前Activity已经发生内存泄漏了
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);
 //分析activity堆信息
  heapdumpListener.analyze(
      new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,
          gcDurationMs, heapDumpDurationMs));
}
return DONE;
}
3.5.1、RefWatcher.removeWeaklyReachableReferences()
private void removeWeaklyReachableReferences() {
KeyedWeakReference ref;
while ((ref = (KeyedWeakReference) queue.poll()) != null) {
  retainedKeys.remove(ref.key);
}
}

这个主要判断当前Activity是否存在内存泄露。原理就是:
通过创建WeakRrefences对象,它包含一个queue,当jvm开始gc时,如果WeakRrefences包含的对象被回收,则会加入到这个queue中。那么通过这个queue就可以知道当前的这个实例是否被回收了还是没有被回收。

四、总结

原理也比较简单在触发GC时
利用了WeakRrefences和WeakRrefencesQueue判断某个对象是否已经被回收。

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

推荐阅读更多精彩内容

  • LeakCanary原理浅析 1.LeakCanary简介 LeakCanary是一个Android和Java的内...
    chewbee阅读 6,816评论 2 38
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,050评论 25 707
  • 说到内存泄漏,就必须提到LeakCanary. 这个利器,很方便的显示出内存泄漏的地方。在用到的过程中好奇怎...
    David_zhou阅读 1,193评论 0 0
  • 北京是无数学子向往的求学圣地。近期我也在北京晃荡,读读书,蹭蹭课,聆听一下讲座。自2月份以来,北京下雨的天非常少,...
    问津学术阅读 795评论 0 0
  • 我没有实际想象过我会经历怎样的婆媳关系,但朋友以及网络上的故事总是在提醒我:婆媳关系是世上最难处理的3大关系之一。...
    郭琳静Grace阅读 246评论 0 0