博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
LeakCanary源码分析
阅读量:2194 次
发布时间:2019-05-02

本文共 5744 字,大约阅读时间需要 19 分钟。

迁移,欢迎光临

内存泄漏是每个android app都应当重视的东西,在检测内存泄漏这块大家应该都用过LeakCanary这款神器,直接自动检测并形成报告,非常方便查看,github上有直接的使用方式

Demo使用介绍

github上download下代码,直接运行

点击按钮,然后旋转屏幕,没一会就发现leakcanary弹内存泄漏的提示。

为什么内存泄漏了,看下demo代码就知道了

void startAsyncTask() {    // This async task is an anonymous class and therefore has a hidden reference to the outer    // class MainActivity. If the activity gets destroyed before the task finishes (e.g. rotation),    // the activity instance will leak.    new AsyncTask
() { @Override protected Void doInBackground(Void... params) { // Do some slow work in background SystemClock.sleep(20000); return null; } }.execute(); }

点击按钮会开启一个异步线程,异步线程休眠了20s,大家应该都知道这个异步线程是匿名内部类持有外部Activity的this引用,因此当我们旋转屏幕的时候,Activity destroy了,但这个Activity的引用被AsyncTask那个匿名类持有所以无法及时回收导致泄漏。

泄漏原理很简单,但leakcanary又是如何检测出来的呢?

原理浅析

LeakCanary的原理其实非常简单,了解之后对square的大神们简直膜拜,轻巧的设计搞定复杂的问题。正常情况下一个Activity在destroy之后就要销毁,LeakCanary做的就是在一个Activity destroy之后将它放在一个WeakReference中,然后将这个WeakReference关联到一个ReferenceQueue,然后去检测这个ReferenceQueue是否存在这个Queue,不存在就证明这个Activity泄漏了(WeakReference和ReferenceQueue的特性可以百度下,用这种方法检测内存泄漏确实精巧)。

代码剖析

原理大致了解了,该读源码了read the fucking code。

在Application做install操作

public static RefWatcher install(Application application) {    return refWatcher(application).listenerServiceClass(DisplayLeakService.class)//内存泄漏的处理Service        .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())//不需要判断内存泄漏的对象        .buildAndInstall();  }

可以看到是一个比较明显的建造者模式,这里分别构造了发现内存泄漏的处理Service以及不要检测的内存泄漏的对象,这里一般是一些系统类,无需关注。直接看buildAndInstall操作。

/**   * Creates a {@link RefWatcher} instance and starts watching activity references (on ICS+).   */  public RefWatcher buildAndInstall() {    RefWatcher refWatcher = build();    if (refWatcher != DISABLED) {      LeakCanary.enableDisplayLeakActivity(context);      ActivityRefWatcher.install((Application) context, refWatcher);    }    return refWatcher;  }

可以看到这里构造了一个RefWatch,这个是比较重要的一个类,ActivityRefWatcher会最终给Application注册一个生命周期函数回调

private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =      new Application.ActivityLifecycleCallbacks() {        @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {        }        @Override public void onActivityStarted(Activity activity) {        }        @Override public void onActivityResumed(Activity activity) {        }        @Override public void onActivityPaused(Activity activity) {        }        @Override public void onActivityStopped(Activity activity) {        }        @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {        }        @Override public void onActivityDestroyed(Activity activity) {          ActivityRefWatcher.this.onActivityDestroyed(activity);        }      };
void onActivityDestroyed(Activity activity) {    refWatcher.watch(activity);  }

最重点的watch方法来了。

Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {    long gcStartNanoTime = System.nanoTime();    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);    removeWeaklyReachableReferences();    if (debuggerControl.isDebuggerAttached()) {      // The debugger can create false leaks.      return RETRY;    }    if (gone(reference)) {      return DONE;    }    gcTrigger.runGc();    removeWeaklyReachableReferences();    if (!gone(reference)) {      long startDumpHeap = System.nanoTime();      long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);	//获取dump文件      File heapDumpFile = heapDumper.dumpHeap();      if (heapDumpFile == RETRY_LATER) {        // Could not dump the heap.        return RETRY;      }      long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);      //分析dump文件      heapdumpListener.analyze(          new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,              gcDurationMs, heapDumpDurationMs));    }    return DONE;  }

会先进行一次是否被回收的判断(gone方法),没被回收,触发gc操作,再检测是否被回收,如果没被回收就dump内存快照,heapDumper.dumpHeap();

public File dumpHeap() {    File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();    if (heapDumpFile == RETRY_LATER) {      return RETRY_LATER;    }    FutureResult
waitingForToast = new FutureResult<>(); showToast(waitingForToast); if (!waitingForToast.wait(5, SECONDS)) { CanaryLog.d("Did not dump heap, too much time waiting for Toast."); return RETRY_LATER; } Toast toast = waitingForToast.get(); try { Debug.dumpHprofData(heapDumpFile.getAbsolutePath()); cancelToast(toast); return heapDumpFile; } catch (Exception e) { CanaryLog.d(e, "Could not dump heap"); // Abort heap dump return RETRY_LATER; } }

dump出文件之后,会把结果交个一个IntentHandler处理

@Override protected void onHandleIntent(Intent intent) {    if (intent == null) {      CanaryLog.d("HeapAnalyzerService received a null intent, ignoring.");      return;    }    String listenerClassName = intent.getStringExtra(LISTENER_CLASS_EXTRA);    HeapDump heapDump = (HeapDump) intent.getSerializableExtra(HEAPDUMP_EXTRA);    HeapAnalyzer heapAnalyzer = new HeapAnalyzer(heapDump.excludedRefs);	//分析dump结果    AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);    AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);  }

分析过程主要是使用一个haha库进行分析,这个库就不做分析了,将分析得到的可能泄漏的问题回调回去,整个leakcanary基本就这么分析的,原理还是蛮简单的。

功能扩展

了解了LeakCanary的原理之后,发现其实它就是在对象不可用的时候去判断对象是否被回收了,但leakcanary只检查了Activity,我们是否可以检查其他对象呢,毕竟Activity泄漏只是内存泄漏的一种,答案当然是可以的,我们只要需要进行如下操作

LeakCanary.install(app).watch(object)

但调用这个方法有个前提就是,我们在调用这个方法的时候确定了这个object已经不需要了,可以被回收了才能调用这个方法,通过这种方式我们就可以对任何对象都进行检测了。

转载地址:http://mysub.baihongyu.com/

你可能感兴趣的文章
PHPstudy中遇到的坑No input file specified,以及传到linux环境下遇到的坑,模板文件不存在
查看>>
TP5.1事务操作和TP5事务回滚操作多表
查看>>
composer install或composer update 或 composer require phpoffice/phpexcel 失败解决办法
查看>>
TP5.1项目从windows的Apache服务迁移到linux的Nginx服务需要注意几点。
查看>>
win10安装软件 打开时报错 找不到 msvcp120.dll
查看>>
PHPunit+Xdebug代码覆盖率以及遇到的问题汇总
查看>>
PHPUnit安装及使用
查看>>
PHP项目用xhprof性能分析(安装及应用实例)
查看>>
composer安装YII
查看>>
Sublime text3快捷键演示
查看>>
sublime text3 快捷键修改
查看>>
关于PHP几点建议
查看>>
硬盘的接口、协议
查看>>
VLAN与子网划分区别
查看>>
Cisco Packet Tracer教程
查看>>
02. 交换机的基本配置和管理
查看>>
03. 交换机的Telnet远程登陆配置
查看>>
微信小程序-调用-腾讯视频-解决方案
查看>>
phpStudy安装yaf扩展
查看>>
密码 加密 加盐 常用操作记录
查看>>