首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >LeakCanary源码浅析

LeakCanary源码浅析

作者头像
用户1337002
发布2018-04-18 17:32:21
6850
发布2018-04-18 17:32:21
举报
文章被收录于专栏:猿份到猿份到

在Android开发中最让人们头疼的就是内存泄漏了,今天来介绍一个查看内存是否泄漏的工具LeakCanary,并通过研究源码明白它是如何分析和查找存在泄漏信息的

首先送上LeakCanary文档链接:[LeakCanary中文使用说明](https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/)

  • Part1. 知识回顾
  • 常用工具

1. Mat

2. LeakCanary(Square)

原理:watch监视一个即将要销毁的对象

  • 内存种类

1、栈(stack-基本数据类型,对象的引用)

2、堆(heap-存放new出来的对象和数组,在堆中分配内存由GC管理)

3、方法区(method,大体和堆一样)

  • 为什么会产生内存泄漏

· 当一个对象已经不需要再使用了,在该对象被回收时候,有另外的对象引用该回收对象,导致本该被回收的对象无法回收

· 有些对象只有有限的生命周期,当生命周期很短的完成任务后,在本该结束的生命周期中仍然被引用

  • 内存泄漏会导致什么问题

1. OOM

  • 常见的内存泄漏情况

1. 单例造成的内存泄漏

2. 非静态内部类创建静态实例造成的内存泄漏

3. handler造成内存泄漏(handler、message、MessageQueue)

解决方法:

①将Handler声明为静态类型

②通过弱引用的方式引入Activity

4. 线程造成的内存泄漏(解决方法:将线程定义声明为static类型)

5. webview造成的内存泄漏(example:加载页面很复杂,Ex:大量的图片)

  • Part2 概念
  • 引用类型

1. 强引用(StrongReference),默认对象一般为强引用

2. 软引用(SoftReference),当内存空间足够大时相当于强引用,内存不够时通过垃圾回收器(GC)自动回收

3. 弱引用(WeakReference),当GC扫描到该类型的引用时就自己回收

4. 虚引用,相当于没有进行引用,GC可随时回收该类型的引用

  • ReferenceQueue

1. 软引用和弱引用都持有该对象

2. 对象被垃圾回收,Java虚拟机就会把这个引用加入到与之相关联的引用队列中

  • Part3.LeakCanary使用

1. 在module层级中的build.gradle中加入引用,不同的编译使用不同的引用

dependencies {
    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
}

2.在Application中:

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        LeakCanary.install(this);
    }
}

3.在Manifest.xml中加载该Application文件

<application android:name=".MyApplication">
  • Part4. LeakCanary源码剖析

从代码入口剖析:

LeakCanary.install(this);

跟踪源码可知

/**
 * Creates a {@link RefWatcher} that works out of the box, and starts watching activity
 * references (on ICS+).
 */
public static RefWatcher install(Application application) {
  return install(application, DisplayLeakService.class);
}

从上面的代码我们发现这个方法最终返回给我们一个RefWatcher这个类,这个类是主要是启动ActivityRefWatcher类,ActivityRefWatcher在Activity的onDestory方法结束时检测内存泄漏。

看下install这个方法:

/**
 * Creates a {@link RefWatcher} that reports results to the provided service, and starts watching
 * activity references (on ICS+).
 */
public static RefWatcher install(Application application,
    Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
  if (isInAnalyzerProcess(application)) {
    return RefWatcher.DISABLED;
  }
  enableDisplayLeakActivity(application);
  HeapDump.Listener heapDumpListener =
      new ServiceHeapDumpListener(application, listenerServiceClass);
  RefWatcher refWatcher = androidWatcher(application, heapDumpListener);
  ActivityRefWatcher.installOnIcsPlus(application, refWatcher);
  return refWatcher;
}

通过RefWatcher refWatcher = androidWatcher(application, heapDumpListener)创建一个RefWatcher对象,启动activityRefWatcher来监视内存泄漏

enableDisplayLeakActivity(application)主要作用是开启DisplayLeakActivity这个类,这个类主要是显示内存泄漏的弹框页面

ActivityRefWatcher.installOnIcsPlus(application, refWatcher);
public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {
  if (SDK_INT < ICE_CREAM_SANDWICH) {
    // If you need to support Android < ICS, override onDestroy() in your base activity.
    return;
  }
  ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
  activityRefWatcher.watchActivities();
}
public void watchActivities() {
  // Make sure you don't get installed twice.
  stopWatchingActivities();
  application.registerActivityLifecycleCallbacks(lifecycleCallbacks);
}

public void stopWatchingActivities() {
  application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);
}

小结:

①通过stopWatcher方法反注册以前的Activity的生命周期的callback,目的是为了保证以前的内存泄漏的activity删除

②重新注册activity生命周期的callback

③通过lifecycleCallbacks中的onActivityDestroyed方法将activity的生命周期和ActivityReference关联起来

@Override public void onActivityDestroyed(Activity activity) {
  ActivityRefWatcher.this.onActivityDestroyed(activity);
}

跟踪onActivityDestroyed方法发现通过调用RefWatcher调用了watcher类

void onActivityDestroyed(Activity activity) {
  refWatcher.watch(activity);
}
private final RefWatcher refWatcher;

下面我们进入RefWatcher类中发现有如下的变量信息

private final Executor watchExecutor;
private final DebuggerControl debuggerControl;
private final GcTrigger gcTrigger;
private final HeapDumper heapDumper;
private final Set<String> retainedKeys;
private final ReferenceQueue<Object> queue;
private final HeapDump.Listener heapdumpListener;
  • 上述变量大意如下:

1. watchExecutor主要用于执行内存泄漏检测

2. debuggerControl查询我们是否正在调试中,如果我们正在调试过程中则不会进行判断

3. gcTrigger用于处理GC,用于在判断泄漏对象之前再调用GC类中的方法再次判断

4. heapDumper用于dump中内存泄漏堆文件

5. retainedKeys该set集合持有待检测和已产生内存泄漏信息的key

6. queue引用对象,主要是判断弱引用所持有的对象是否已执行GC垃圾收回

7. heapdumpListener主要用于分析产生hprof文件回调

查看watch方法可知:

/**
 * Watches the provided references and checks if it can be GCed. This method is non blocking,
 * the check is done on the {@link Executor} this {@link RefWatcher} has been constructed with.
 *
 * @param referenceName An logical identifier for the watched object.
 */
public void watch(Object watchedReference, String referenceName) {
  checkNotNull(watchedReference, "watchedReference");
  checkNotNull(referenceName, "referenceName");
  if (debuggerControl.isDebuggerAttached()) {
    return;
  }
  final long watchStartNanoTime = System.nanoTime();
  String key = UUID.randomUUID().toString();
  retainedKeys.add(key);
  final KeyedWeakReference reference =
      new KeyedWeakReference(watchedReference, key, referenceName, queue);

  watchExecutor.execute(new Runnable() {
    @Override public void run() {
      ensureGone(reference, watchStartNanoTime);
    }
  });
}

通过产生一个唯一的key添加到retainedKeys<Set>集合中

String key = UUID.randomUUID().toString();
retainedKeys.add(key);

再创建一个KeyedWeakReference的弱引用,并开启一个异步线程来分析创建好的弱引用,该线程主要作用是确保我们的Activity是否真正已经进入到GONE状态

void ensureGone(KeyedWeakReference reference, long watchStartNanoTime) {
  long gcStartNanoTime = System.nanoTime();
  //计算过去的方法到调用GC垃圾收回的时间值
  long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
  //清除已经到达我们引用队列的弱引用 
  removeWeaklyReachableReferences();
 //判断如果处于debug状态就不再进行内存分析
  if (gone(reference) || debuggerControl.isDebuggerAttached()) {
    return;
  }
 //手动进行垃圾回收    
  gcTrigger.runGc();
  
  removeWeaklyReachableReferences();
  if (!gone(reference)) {
    long startDumpHeap = System.nanoTime();
    long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
   //dump出内存泄漏的文件
    File heapDumpFile = heapDumper.dumpHeap();

    if (heapDumpFile == null) {
      // Could not dump the heap, abort.
      return;
    }
    long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
 //开始分析内存泄漏文件查找内存泄漏路径
    heapdumpListener.analyze(
        new HeapDump(heapDumpFile, reference.key, reference.name, watchDurationMs, gcDurationMs,
            heapDumpDurationMs));
  }
}

以上代码部分总结如下:

1. 首先创建一个RefWatcher,启动一个ActivityRefWatcher

2. 通过ActivityLifecyclecallback将Activity的onDestroy生命周期给关联起来

3. 最后通过执行execute线程来分析泄漏信息

  • 探讨LeakCanary中Activity泄漏检测机制代码

在上面的ensureGone方法中最后我们发现有这样的代码

heapdumpListener.analyze(
    new HeapDump(heapDumpFile, reference.key, reference.name, watchDurationMs, gcDurationMs,
        heapDumpDurationMs));

通过跟踪发现analyze方法该方法是HeapDump类中的一个interface接口,再查看它的实现类发现在ServiceHeapDumpListener这个类中的方法

@Override public void analyze(HeapDump heapDump) {
  checkNotNull(heapDump, "heapDump");
  HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass);
}

继续跟踪runAnalysis方法发现在HeapAnalyzerService中,且该类继承了intentService,因此它将会每次调用onHandleIntent方法

@Override protected void onHandleIntent(Intent intent) {
  String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);
  HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);
  AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);
  AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
}

通过checkForLeak方法来分析内存泄漏信息的结果,并通过sendResultToListener显示最终的结果

/**
 * Searches the heap dump for a {@link KeyedWeakReference} instance with the corresponding key,
 * and then computes the shortest strong reference path from that instance to the GC roots.
 */
public AnalysisResult checkForLeak(File heapDumpFile, String referenceKey) {
  long analysisStartNanoTime = System.nanoTime();

  if (!heapDumpFile.exists()) {
    Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
    return failure(exception, since(analysisStartNanoTime));
  }

  ISnapshot snapshot = null;
  try {
     //生成内存快照信息
    snapshot = openSnapshot(heapDumpFile);
     //查看内存的引用
    IObject leakingRef = findLeakingReference(referenceKey, snapshot);

    // False alarm, weak reference was cleared in between key check and heap dump.
    if (leakingRef == null) {
      return noLeak(since(analysisStartNanoTime));
    }

    String className = leakingRef.getClazz().getName();
    //寻找内存泄漏的路径
    AnalysisResult result =
        findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, className, true);

    if (!result.leakFound) {
      result = findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, className, false);
    }

    return result;
  } catch (SnapshotException e) {
    return failure(e, since(analysisStartNanoTime));
  } finally {
    cleanup(heapDumpFile, snapshot);
  }
}
  • 总结checkForLeak方法

1.把.hprof转为 Snapshot

snapshot = openSnapshot(heapDumpFile);

2.找出泄漏的对象/泄漏对象的最短路径

IObject leakingRef = findLeakingReference(referenceKey, snapshot);
AnalysisResult result =
    findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, className, true);
  • findLeakingReference作用

①在snapshot快照中找到第一个弱引用即为内存发生泄漏的引用

②遍历这个对象的所有实例信息

③如果发现存在key值与之前定义封装好的key值相同,那么返回这个定位到的泄漏对象

  • findLeakTrace是通过获取内存泄漏的引用来获取泄漏路径的最短路径
  • 了解LeakCanary的原理

1. Activity Destroy()之后将它放在一个WeakReference中

2. 将WeakReference关联到一个ReferenceQueue

3. 查看ReferenceQueue是否存有Activity的引用

4. 如果该Activity泄露了,Dump出heap信息,然后去分析泄漏路径

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-03-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 猿份到 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档