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

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

首先送上LeakCanary文档链接:LeakCanary中文使用说明

Part1. 知识回顾常用工具Mat

LeakCanary(Square)

原理:watch监视一个即将要销毁的对象内存种类1、栈(stack-基本数据类型,对象的引用)

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

3、方法区(method,大体和堆一样)为什么会产生内存泄漏当一个对象已经不需要再使用了,在该对象被回收时候,有另外的对象引用该回收对象,导致本该被回收的对象无法回收

有些对象只有有限的生命周期,当生命周期很短的完成任务后,在本该结束的生命周期中仍然被引用内存泄漏会导致什么问题OOM常见的内存泄漏情况单例造成的内存泄漏

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

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

解决方法:①将Handler声明为静态类型

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

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

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

Part2 概念引用类型强引用(StrongReference),默认对象一般为强引用

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

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

虚引用,相当于没有进行引用,GC可随时回收该类型的引用ReferenceQueue软引用和弱引用都持有该对象

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

Part3.LeakCanary使用在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文件

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

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 retainedKeys;  private final ReferenceQueue queue;  private final HeapDump.Listener heapdumpListener;

上述变量大意如下:watchExecutor主要用于执行内存泄漏检测

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

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

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

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

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

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集合中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);

File heapDumpFile = heapDumper.dumpHeap();//dump出内存泄漏的文件

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));

}

}

以上代码部分总结如下:首先创建一个RefWatcher,启动一个ActivityRefWatcher

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

最后通过执行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显示最终的结果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转为 Snapshotsnapshot = openSnapshot(heapDumpFile);

2.找出泄漏的对象/泄漏对象的最短路径IObject leakingRef = findLeakingReference(referenceKey, snapshot);

AnalysisResult result =

findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, className, true);

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

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

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

findLeakTrace是通过获取内存泄漏的引用来获取泄漏路径的最短路径了解LeakCanary的原理Activity Destroy()之后将它放在一个WeakReference中

将WeakReference关联到一个ReferenceQueue

查看ReferenceQueue是否存有Activity的引用

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

android leakcanary 源码分析,LeakCanary源码浅析相关推荐

  1. 【Android 电量优化】JobScheduler 源码分析 ( JobServiceContext 源码分析 | 闭环操作总结 | 用户提交任务 | 广播接收者接受相关广播触发任务执行 )★

    文章目录 一.JobServiceContext 引入 二.JobServiceContext 源码分析 三.用户在应用层如何使用 JobScheduler 四.用户提交任务 五.广播接收者监听广播触 ...

  2. Android源码分析--MediaServer源码分析(二)

    在上一篇博客中Android源码分析–MediaServer源码分析(一),我们知道了ProcessState和defaultServiceManager,在分析源码的过程中,我们被Android的B ...

  3. Android 事件分发机制分析及源码详解

    Android 事件分发机制分析及源码详解 文章目录 Android 事件分发机制分析及源码详解 事件的定义 事件分发序列模型 分发序列 分发模型 事件分发对象及相关方法 源码分析 事件分发总结 一般 ...

  4. Linux内核 eBPF基础:kprobe原理源码分析:源码分析

    Linux内核 eBPF基础 kprobe原理源码分析:源码分析 荣涛 2021年5月11日 在 <Linux内核 eBPF基础:kprobe原理源码分析:基本介绍与使用>中已经介绍了kp ...

  5. k8s源码分析--kube-scheduler源码(一)

    版本:v1.13.0 启动分析 kubernetes基础组件的入口均在cmd目录下,kube-schduler入口在scheduler.go下. kubernetes所有的组件启动采用的均是comma ...

  6. k8s client-go源码分析 informer源码分析(3)-Reflector源码分析

    k8s client-go源码分析 informer源码分析(3)-Reflector源码分析 1.Reflector概述 Reflector从kube-apiserver中list&watc ...

  7. xf86-video-intel源码分析1 —— 源码目录结构概览

    在<Spectacle/Flameshot/X11 Xlib截屏问题现象及解决方法>一文(链接如下)中提到, Spectacle/Flameshot/X11 Xlib截屏问题现象及解决方法 ...

  8. 【Android 热修复】热修复原理 ( 类加载分析 | 分析 PathClassLoader 源码 | 分析 BaseDexClassLoader 源码 | 分析 PathDexList 源码 )

    文章目录 一.分析 PathClassLoader 源码 二.分析 BaseDexClassLoader 源码 三.分析 PathDexList 源码 四. 源码资源 一.分析 PathClassLo ...

  9. 【Android 电量优化】JobScheduler 相关源码分析 ( JobSchedulerService 源码分析 | 任务检查 | 任务执行 )

    文章目录 一.回调 StateChangedListener 接口 二.JobHandler 处理 ( 任务检查 ) 三.maybeRunPendingJobsH 方法 四.assignJobsToC ...

最新文章

  1. python中where函数_如何在python中基于Where函数获取两列值
  2. 推荐系统中基于深度学习的混合协同过滤模型
  3. poj2186 求有向图G中所有点都能到达的点的数量
  4. Java黑皮书课后题第8章:*8.2(求矩阵对角线元素的和)使用下面的方法头编写一个方法,求n*n的double类型矩阵中主对角线上所有数字的和。编写一个程序,读取一个4*4的矩阵,显示主对角线和
  5. springboot shiro ajax,SpringBoot Shiro 登录成功后返回json数据 shiro使用ajax登录
  6. 微信硬件平台对接--蓝牙
  7. 基于SSM的企业员工管理系统
  8. java找出两个共有,Java-找出两个单链表的首个公共节点
  9. 汽车CAN总线系统原理、设计与应用 第二章CAN总线的基本原理 ---- 个人自学笔记
  10. 惯导标定国内外研究现状小结(删减版)
  11. linux unlink 函数,linux – unlink和rm有什么区别?
  12. Linux中卷的扩展命令
  13. 跟未名学Office - PPT操作:高效
  14. Linux主分区文件系统,Linux_Linux磁盘和文件系统管理,1、 分区MBR(Master Boot Recor - phpStudy...
  15. Word去除目录主页页码
  16. 前端学习资料(禅意花园,菜鸟教程)
  17. ASA5520升级内存记录
  18. vivado导入tcl例程
  19. excel 连接sql server失败
  20. Windows/Mac下 Eclipse中最常用的热键

热门文章

  1. 直播平台流媒体服务器搭建(Linux+Nginx+RTMP)
  2. 使用MATLAB将raw格式图片转换为txt
  3. 基于JavaWeb的旅游信息管理系统设计与实现
  4. 【科创人独家】科界CTO林镇南:言必真,行必果,没有尽力而为,只有全力以赴...
  5. ruby on rails_最终的中级Ruby on Rails教程:让我们创建一个完整的应用程序!
  6. 华为3COM路由器交换机配置命令详解
  7. SP技术培训班(二):IVR、SMS、MSM、WAP、MO、MT。。。
  8. 计算机组成与体系结构乘法指令设计,基于Quartus II的计算机组成与体系结构综合实验教程...
  9. 数值策划入门指南(一):做数值就是做体验
  10. Java图片加文字水印