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

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

前言

最近高产似母猪,闲下来的时候就喜欢找找源码看。昨天看了下LeakCanary,准备来分析一波。

导入

gradle文件中添加:

debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4' // debug    releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4' // release

初始化

Application中通过LeakCanary.install(this);进行初始化。

public static RefWatcher install(Application application) {    return refWatcher(application).listenerServiceClass(DisplayLeakService.class)            .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())            .buildAndInstall();}// 创建AndroidRefWatcherBuilder对象public static AndroidRefWatcherBuilder refWatcher(Context context) {    return new AndroidRefWatcherBuilder(context);}// 只是保存applicationContextAndroidRefWatcherBuilder(Context context) {    this.context = context.getApplicationContext();}// 服务监听public AndroidRefWatcherBuilder listenerServiceClass(        Class
listenerServiceClass) { return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));}// 堆内存镜像监听服务public final class ServiceHeapDumpListener implements HeapDump.Listener { private final Context context; private final Class
listenerServiceClass; public ServiceHeapDumpListener(Context context, Class
listenerServiceClass) { setEnabled(context, listenerServiceClass, true); setEnabled(context, HeapAnalyzerService.class, true); this.listenerServiceClass = checkNotNull(listenerServiceClass, "listenerServiceClass"); this.context = checkNotNull(context, "context").getApplicationContext(); } @Override public void analyze(HeapDump heapDump) { checkNotNull(heapDump, "heapDump"); HeapAnalyzerService.runAnalysis(context, heapDump, listenerServiceClass); }}

这里有两个重点:

  1. AndroidRefWatcherBuilder保存了ServiceHeapDumpListener监听器。
  2. ServiceHeapDumpListener监听的服务是DisplayLeakService

在初始化时,AndroidExcludedRefs.createAppDefaults().build()创建了默认忽略内存泄漏的属性,这里不多说。

多说一句,这里面有个代码是setEnabled,其最终实现是:

public static void setEnabledBlocking(Context appContext, Class
componentClass, boolean enabled) { ComponentName component = new ComponentName(appContext, componentClass); PackageManager packageManager = appContext.getPackageManager(); int newState = enabled ? COMPONENT_ENABLED_STATE_ENABLED : COMPONENT_ENABLED_STATE_DISABLED; // Blocks on IPC. packageManager.setComponentEnabledSetting(component, newState, DONT_KILL_APP);}

这个主要就是设置组件可用状态,因为这里面默认的许多组件都是不可用状态,下面的Service以及Activity的enabled属性都为false:

img_a0f328fbf2846c7cbaf9d36ac6659747.png
不可用
接着看下
buildAndInstall()方法:

public RefWatcher buildAndInstall() {    RefWatcher refWatcher = build();    if (refWatcher != DISABLED) {        LeakCanary.enableDisplayLeakActivity(context);        // 将Application和ActivityRefWatcher进行绑定        ActivityRefWatcher.install((Application) context, refWatcher);    }    return refWatcher;}public static void install(Application application, RefWatcher refWatcher) {    new ActivityRefWatcher(application, refWatcher).watchActivities();}private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =        new Application.ActivityLifecycleCallbacks() {            // 省略其他生命周期,只有在onActivityDestroyed会调用            ......                        @Override            public void onActivityDestroyed(Activity activity) {                ActivityRefWatcher.this.onActivityDestroyed(activity);            }        };// 注册对Activity的监听public void watchActivities() {    // Make sure you don't get installed twice.    stopWatchingActivities();    application.registerActivityLifecycleCallbacks(lifecycleCallbacks);}

到这里就知道LeakCanary是如何监听页面销毁的了,和Lifecycle一样,通过注册Application.ActivityLifecycleCallbacks实现对Activity生命周期的监听,在destory的时候进行内存分析。

内存分析

从上面看到,当Activity执行了onDestory生命周期后,就会触发ActivityRefWatcher.this.onActivityDestroyed(activity),这里有几个方法调用:

void onActivityDestroyed(Activity activity) {    refWatcher.watch(activity);}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();    // 将随机生成的key保存    retainedKeys.add(key);    // 创建了一个存储了随机生成的key的弱引用,并且传入了ReferenceQueue    final KeyedWeakReference reference =            new KeyedWeakReference(watchedReference, key, referenceName, queue);    ensureGoneAsync(watchStartNanoTime, reference);}private void ensureGoneAsync(final long watchStartNanoTime, final KeyedWeakReference reference) {    // watchExecutor是AndroidWatchExecutor    watchExecutor.execute(new Retryable() {        @Override public Retryable.Result run() {            return ensureGone(reference, watchStartNanoTime);        }    });}

这里将我们传入的Activity通过弱引用的方式保存起来KeyedWeakReference,并且还将随机生成一个key作为改Activity的唯一标志。

我们在构造AndroidRefWatcher时,创建的是 AndroidWatchExecutorAndroidWatchExecutor根据返回的结果Retryable.Result进行判断,如果为DONE,则代表处理完成;如果为RETRY,则需要重新处理。

img_8fe409177ae69c33b1040623898a043f.png
AndroidWatchExecutor
接着看下
ensureGone的操作吧:

Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {    long gcStartNanoTime = System.nanoTime();    long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);    // 移除掉已经被释放的Activity对应的key    removeWeaklyReachableReferences();    // debug状态    if (debuggerControl.isDebuggerAttached()) {        // The debugger can create false leaks.        return RETRY;    }    // 判断是否结束    if (gone(reference)) {        return DONE;    }    // 手动调用gc,这里会使线程休眠100ms    gcTrigger.runGc();    // 再次移除已经被释放的Activity对应的key    removeWeaklyReachableReferences();    // 如果还是存在key,说明有内存泄漏    if (!gone(reference)) {        long startDumpHeap = System.nanoTime();        long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);        // 获取heapDumpFile文件,这里heapDumper是AndroidHeapDumper,会弹出那个经典的toast        File heapDumpFile = heapDumper.dumpHeap();        if (heapDumpFile == RETRY_LATER) {            // Could not dump the heap.            return RETRY;        }        long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);        // 调用ServiceHeapDumpListener的analyze方法        heapdumpListener.analyze(                new HeapDump(heapDumpFile, reference.key, reference.name, excludedRefs, watchDurationMs,                        gcDurationMs, heapDumpDurationMs));    }    return DONE;}private void removeWeaklyReachableReferences() {    // 弱引用对应的资源被释放,会将弱引用放到Quene中,通过poll可以出队列    KeyedWeakReference ref;    while ((ref = (KeyedWeakReference) queue.poll()) != null) {        // 将KeyedWeakReference对应的key移除        retainedKeys.remove(ref.key);    }}// 是否结束的标志是保存的key中是否存在当前弱引用的keyprivate boolean gone(KeyedWeakReference reference) {    return !retainedKeys.contains(reference.key);}

流程如下:

  1. 移除已经被回收的Activity,并且移除Activity对应的key
  2. 判断是否还有对应的key,如果没有则返回DONE
  3. 此时还有对应的key,调用一次gc,这里会使线程休眠100ms
  4. 进行1和2的操作,如果仍然有对应的key,则调用AndroidHeapDumper生成文件,并且调用之前传入的ServiceHeapDumpListener.analyze()方法

分析过程

ServiceHeapDumpListeneranalyze()方法:

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

其中listenerServiceClass是我们创建时添加的类,其类为DisplayLeakService(后面会用到)。

runAnalysis方法:

public static void runAnalysis(Context context, HeapDump heapDump,                               Class
listenerServiceClass) { Intent intent = new Intent(context, HeapAnalyzerService.class); intent.putExtra(LISTENER_CLASS_EXTRA, listenerServiceClass.getName()); intent.putExtra(HEAPDUMP_EXTRA, heapDump); context.startService(intent);}

该方法只是启动了一个继承了IntentServiceHeapAnalyzerService,可以看下其onHandleIntent方法:

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

这里面主要的分析过程时通过HeapAnalyzer的checkForLeak方法实现的:

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));    }    try {        // 将保存的hprof文件转为Snapshot对象,这里通过com.squareup.haha实现        HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);        HprofParser parser = new HprofParser(buffer);        Snapshot snapshot = parser.parse();        deduplicateGcRoots(snapshot);        // 查找泄漏的引用        Instance leakingRef = findLeakingReference(referenceKey, snapshot);        // False alarm, weak reference was cleared in between key check and heap dump.        if (leakingRef == null) {            return noLeak(since(analysisStartNanoTime));        }        // 获取引用的跟踪信息        return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef);    } catch (Throwable e) {        return failure(e, since(analysisStartNanoTime));    }}private Instance findLeakingReference(String key, Snapshot snapshot) {    // 获得KeyedWeakReference类的引用    ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());    List
keysFound = new ArrayList<>(); for (Instance instance : refClass.getInstancesList()) { List
values = classInstanceValues(instance); String keyCandidate = asString(fieldValue(values, "key")); // 如果某类的key和我们传入的key相同,那么就是这个类泄漏了 if (keyCandidate.equals(key)) { return fieldValue(values, "referent"); } keysFound.add(keyCandidate); } throw new IllegalStateException( "Could not find weak reference with key " + key + " in " + keysFound);}

最终根据泄漏的类来进行引用跟踪,并且返回AnalysisResult分析结果。

这里时通过com.squareup.haha来进行hropf文件分析以及引用树的建立,其内存泄漏的分析应该和MAT等工具原理一致。

显示结果

构造好了AnalysisResult分析结果后,通过AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);方法将结果发送到listenerClassName,这个就是我们上面说的DisplayLeakService,顾名思义,这个就是用于显示泄漏的Service:

img_daf7b54556d28ababcb8b97f02bbe65a.png
核心代码

总结

这里最主要的内存泄漏分析过程并没有写出来,主要是其使用了另一个库进行分析,实在是没有精力去分析了。

简单总结下:

  1. 何时调用内存泄漏分析?

通过注册application.registerActivityLifecycleCallbacks注册对Activity生命周期回调的监听,在onDestory后进行内存泄漏分析

  1. 如何确定可能有内存泄漏?

通过KeyedWeakReferenceReferenceQueue对弱引用进行是否回收判断,如果回收则将该弱引用放入ReferenceQueue中。

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

你可能感兴趣的文章
Java下数字类型的转换
查看>>
DNS原理及DNS服务器的建立(主从)
查看>>
我的友情链接
查看>>
mongodb的安装及主从复制
查看>>
VMware虚拟化技术培训(10) 桌面虚拟化之二
查看>>
Win7旗舰版中的IIS配置asp.net的运行环境
查看>>
Stimulsoft Reports.Net基础教程(八):创建列式报表②
查看>>
Maven
查看>>
Newbit的引脚图
查看>>
sql server使用组合索引需要注意的地方
查看>>
quartz (从原理到应用)详解篇
查看>>
面向对象编程6大设计原则:开放封闭责原则
查看>>
jena RDF学习笔记
查看>>
JDK的环境配置
查看>>
zabbix使用percona plugin监控mysql
查看>>
软工网络15Alpha阶段敏捷冲刺博客汇总
查看>>
centos5.6 x32安装oracle11
查看>>
Smokeping 监控搭建
查看>>
Centos7.2安装和配置Tomcat8
查看>>
我的友情链接
查看>>