文章目录

  • 介绍
  • 使用
  • 问题
  • 源码分析
  • 总结

介绍

内存泄漏检测工具,square公司出品,github地址https://github.com/square/leakcanary。
gradle引用:

debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'

使用

在自定义application中onCreat方法中调用下面代码。默认只是检测所有activity中的内存泄漏。如果想检测fragment、service等的内存泄漏,需要在相应的类的onDestory方法中调用refWatcher.watch(this)即可。然后在使用app的过程中,如果出现内存泄漏就会在leaks的app中listview展示出来,并且有相应的路径。

  if (LeakCanary.isInAnalyzerProcess(this)) {// This process is dedicated to LeakCanary for heap analysis.// You should not init your app in this process.return;}LeakCanary.install(this);

问题

使用特别简单,但是想了想,有几个问题值得思考。
如何检测到所有activity的内存泄漏的?
内存泄漏检测机制是什么?
内存泄漏轨迹的生成过程 ?

源码分析

    public static RefWatcher install(Application application) {return refWatcher(application)//创建引用监听者.listenerServiceClass(DisplayLeakService.class)//显示泄漏信息service.excludedRefs(AndroidExcludedRefs.createAppDefaults().build()).buildAndInstall();}

以上为install源码,不难发现buildAndInstall是一个build模式,一般build都是一些校验或者初始化。

    public RefWatcher buildAndInstall() {RefWatcher refWatcher = build();if (refWatcher != DISABLED) {LeakCanary.enableDisplayLeakActivity(context);ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher);}return refWatcher;}

然后build之后创建一个RefWatcher对象。

    public final RefWatcher build() {if (isDisabled()) {return RefWatcher.DISABLED;}ExcludedRefs excludedRefs = this.excludedRefs;if (excludedRefs == null) {excludedRefs = defaultExcludedRefs();}HeapDump.Listener heapDumpListener = this.heapDumpListener;if (heapDumpListener == null) {heapDumpListener = defaultHeapDumpListener();}DebuggerControl debuggerControl = this.debuggerControl;if (debuggerControl == null) {debuggerControl = defaultDebuggerControl();}HeapDumper heapDumper = this.heapDumper;if (heapDumper == null) {heapDumper = defaultHeapDumper();}WatchExecutor watchExecutor = this.watchExecutor;if (watchExecutor == null) {watchExecutor = defaultWatchExecutor();}GcTrigger gcTrigger = this.gcTrigger;if (gcTrigger == null) {gcTrigger = defaultGcTrigger();}return new RefWatcher(watchExecutor, debuggerControl, gcTrigger, heapDumper, heapDumpListener,excludedRefs);}

创建完对象之后,enableDisplayLeakActivity方法是否展示泄漏activity,最重要的是ActivityRefWatcher.installOnIcsPlus((Application) context, refWatcher)处理。

    @TargetApi(ICE_CREAM_SANDWICH) public final class ActivityRefWatcher {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();}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);}};private final Application application;private final RefWatcher refWatcher;/*** Constructs an {@link ActivityRefWatcher} that will make sure the activities     are not leaking* after they have been destroyed.*/public ActivityRefWatcher(Application application, final RefWatcher refWatcher) {this.application = checkNotNull(application, "application");this.refWatcher = checkNotNull(refWatcher, "refWatcher");}void onActivityDestroyed(Activity activity) {refWatcher.watch(activity);}public void watchActivities() {// Make sure you don't get installed twice.stopWatchingActivities();application.registerActivityLifecycleCallbacks(lifecycleCallbacks);}public void stopWatchingActivities() {application.unregisterActivityLifecycleCallbacks(lifecycleCallbacks);}}

installOnIcsPlus方法先判断版本<14 不能使用,然后调用创建ActivityRefWatcher对象,调用watchActivities方法,watchActivities里面就是将传入的application注册registerActivityLifecycleCallbacks用来获取这个application的所有activity的生命周期。然后在Application.ActivityLifecycleCallbacks回调中onActivityDestroyed生命周期里面调用ActivityRefWatcher.this.onActivityDestroyed(activity)方法,最后调用到refWatcher.watch(activity),接下来进入watch来看下。

    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);final KeyedWeakReference reference =new KeyedWeakReference(watchedReference, key, referenceName, queue);ensureGoneAsync(watchStartNanoTime, reference);}

首先根据uuid创建一个key放入retainedKeys数组里面,添加弱引用的标识,KeyedWeakReference是继承WeakReference的类,一个弱引用的封装。看到
new KeyedWeakReference(watchedReference, key, referenceName, queue),是在创建一个弱引用,并放到ReferenceQueue引用队列中。KeyedWeakReference里面日常参数校验,以及调用WeakReference的super方法。

    final class KeyedWeakReference extends WeakReference<Object> {public final String key;public final String name;KeyedWeakReference(Object referent, String key, String name,ReferenceQueue<Object> referenceQueue) {super(checkNotNull(referent, "referent"), checkNotNull(referenceQueue, "referenceQueue"));this.key = checkNotNull(key, "key");this.name = checkNotNull(name, "name");}}

接下来查看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);//清除此时已经到ReferenceQueue中的弱引用removeWeaklyReachableReferences();if (debuggerControl.isDebuggerAttached()) {// The debugger can create false leaks.return RETRY;}//没有内存泄漏if (gone(reference)) {return DONE;}//gc处理gcTrigger.runGc();removeWeaklyReachableReferences();//代表内存泄漏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);//生成hprof文件并进行分析hprof文件heapdumpListener.analyze(new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,gcDurationMs, heapDumpDurationMs));}return DONE;}

源码大概就是先清除弱引用的队列,然后查看引用是否存在队列中,
.if (gone(reference)) 用来判断某个 reference 的 key 是否仍在 retainedKeys 里,若不在,表示已回收,否则继续。然后在手动gc,之后再清除弱引用,在查看是否弱引用队列中是否存在,如果存在证明有内存泄露,就生成hprof文件并分析hprof文件。
removeWeaklyReachableReferences方法是清除此时已经到ReferenceQueue中的弱引用,gone方法是检测存放key的数组是否存在key。

    private boolean gone(KeyedWeakReference reference) {return !retainedKeys.contains(reference.key);}private void removeWeaklyReachableReferences() {// WeakReferences are enqueued as soon as the object to which they point to becomes weakly// reachable. This is before finalization or garbage collection has actually happened.KeyedWeakReference ref;while ((ref = (KeyedWeakReference) queue.poll()) != null) {retainedKeys.remove(ref.key);}}File heapDumpFile = heapDumper.dumpHeap(),生成hprof文件的具体实现在AndroidHeapDumper类中。public final class AndroidHeapDumper implements HeapDumper {private final Context context;private final LeakDirectoryProvider leakDirectoryProvider;private final Handler mainHandler;public AndroidHeapDumper(Context context, LeakDirectoryProvider   leakDirectoryProvider) {this.leakDirectoryProvider = leakDirectoryProvider;this.context = context.getApplicationContext();mainHandler = new Handler(Looper.getMainLooper());}@Override public File dumpHeap() {File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();if (heapDumpFile == RETRY_LATER) {return RETRY_LATER;}FutureResult<Toast> 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 dumpreturn RETRY_LATER;}}private void showToast(final FutureResult<Toast> waitingForToast) {mainHandler.post(new Runnable() {@Override public void run() {final Toast toast = new Toast(context);toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);toast.setDuration(Toast.LENGTH_LONG);LayoutInflater inflater = LayoutInflater.from(context);toast.setView(inflater.inflate(R.layout.leak_canary_heap_dump_toast, null));toast.show();// Waiting for Idle to make sure Toast gets rendered.Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {@Override public boolean queueIdle() {waitingForToast.set(toast);return false;}});}});}private void cancelToast(final Toast toast) {mainHandler.post(new Runnable() {@Override public void run() {toast.cancel();}});}}分析的具体实现在HeapAnalyzerService中,HeapAnalyzerService是一个IntentService。public final class HeapAnalyzerService extends IntentService {private static final String LISTENER_CLASS_EXTRA = "listener_class_extra";private static final String HEAPDUMP_EXTRA = "heapdump_extra";public static void runAnalysis(Context context, HeapDump heapDump,Class<? extends AbstractAnalysisResultService> listenerServiceClass) {Intent intent = new Intent(context, HeapAnalyzerService.class);intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName());intent.putExtra(HEAPDUMP_EXTRA, heapDump);context.startService(intent);}public HeapAnalyzerService() {super(HeapAnalyzerService.class.getSimpleName());}@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);AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey);AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);}}

总结

如何检测到所有activity的内存泄漏的?
通过application注册registerActivityLifecycleCallbacks,来监听说有activity的生命周期,并在onDestory中监听。

内存泄漏检测机制是什么?
若 Activity 被正常回收,就把activity使用KeyedWeakReference创建一个弱引用,引用它的 KeyedWeakReference 会被自动放入 ReferenceQueue 中。首先先移除不可达用引用,如果当前引用不存在,就不继续了,然后gc,再次移除不可达引用,如果当前引用不存在,就不继续了。如果两次移除后都没有被回收,就根据队列的弱引用,生成HeapDump信息。

内存泄漏轨迹的生成过程 ?
利用 heapDumper 把内存情况 dump 成文件,利用 HeapAnalyzer 对 dump 的内存情况进行分析并进一步确认,若确定发生泄漏,调用 DisplayLeakService发送通知。

leakcanary1.5源码分析相关推荐

  1. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  2. SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) SpringBo ...

  3. SpringBoot-web开发(二): 页面和图标定制(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) 目录 一.首页 1. 源码分析 2. 访问首页测试 二.动态页面 1. 动态资源目录t ...

  4. SpringBoot-web开发(一): 静态资源的导入(源码分析)

    目录 方式一:通过WebJars 1. 什么是webjars? 2. webjars的使用 3. webjars结构 4. 解析源码 5. 测试访问 方式二:放入静态资源目录 1. 源码分析 2. 测 ...

  5. Yolov3Yolov4网络结构与源码分析

    Yolov3&Yolov4网络结构与源码分析 从2018年Yolov3年提出的两年后,在原作者声名放弃更新Yolo算法后,俄罗斯的Alexey大神扛起了Yolov4的大旗. 文章目录 论文汇总 ...

  6. ViewGroup的Touch事件分发(源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,View的touch事件分发相对比较简单,可参考 View的Touch事件分发(一.初步了解) View的Touch事 ...

  7. View的Touch事件分发(二.源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...

  8. MyBatis原理分析之四:一次SQL查询的源码分析

    上回我们讲到Mybatis加载相关的配置文件进行初始化,这回我们讲一下一次SQL查询怎么进行的. 准备工作 Mybatis完成一次SQL查询需要使用的代码如下: Java代码   String res ...

  9. [转]slf4j + log4j原理实现及源码分析

    slf4j + log4j原理实现及源码分析 转载于:https://www.cnblogs.com/jasonzeng888/p/6051080.html

最新文章

  1. MSN8.0?!O My God,it's so perfect!
  2. hyperic hq mysql_网络管理工具Hyperic HQ监控MySQL配置
  3. HDU - 4847 Wow! Such Doge!
  4. nmon 安装和使用
  5. 微软发布新的 Azure Pipelines 功能和集成
  6. 2019 牛客多校第三场 B Crazy Binary String
  7. 有了它,从此走上炫酷的编码之路!
  8. computed用发_Vue中的computed属性和nextTick方法
  9. NOIP201501金币
  10. 源码专题之spring设计模式:策略模式、原型模式、模板模式
  11. 根据卫星星历计算卫星坐标——matlab app
  12. 哈希加密matlab,图像加密算法之基于Hash函数和多混沌系统的加密
  13. Phaser 使用介绍
  14. Technica Engineering Enhanced Ethernet Switch, 车载以太网交换机功能介绍(100/1000BASE-T1 Switch, AVB/TSN)
  15. (原创)android6.0系统 PowerManager深入分析
  16. Cathy推荐Java面试题
  17. goLang 如何开发 windows 窗口界面
  18. 杭州 职称 计算机免试,明年杭州市评职称 年满50周岁以上才免考计算机
  19. V4L2编程代码实现以及YUV格式(V4L2二)
  20. 一个数的因子、因数的区别

热门文章

  1. Swift函数式编程十二(表格应用)
  2. 最新jym在线客服源码系统
  3. qml 鼠标点击_qml中设置鼠标样式
  4. java电子日记本代码_计算法日记将规则的价值视为无需计算机的代码
  5. 电子日记本(ediary)
  6. Dell显示器改USB端口供电能力
  7. VS2008 PRO 、WDK 和DDKWizard搭建Vista驱动开发环境
  8. 智能地址填写功能html代码,JS寄快递地址智能解析的实现代码
  9. GDB调试CoreDump文件
  10. BSD是什么系统 BSD简介