美文网首页
[笔记]LeakCanary实现原理概述

[笔记]LeakCanary实现原理概述

作者: 蓝灰_q | 来源:发表于2017-10-15 11:41 被阅读43次

    核心是用RefWatcher在生命周期结束时实现监测,通过JVM弱引用和ReferenceQueue绑定的原理来捕获内存泄露事件,再通过HAHA开源库去分析引用路径,基本步骤包括:
    1.监听生命周期
    通过Application的registerActivityLifecycleCallbacks方法,抓到Activity的ondestroy生命周期,把Activity丢给RefWatcher来监测。
    对于Fragment,就需要在Fragment中手动编写,先从Application中实现RefWatcher,然后在Fragment的onDestroy方法中用refWatcher.watch(this)监控。
    2.工作线程监测对象清除
    启动
    内存泄露检查其实比较耗时,为尽量避免干扰,LeakCanary会在主线程空闲时启动泄露检查,主要是通过在Looper中add一个IdleHandler(在messagequeue的next函数中判断,待处理msg为空,或者没有需要延迟执行的msg,这时会处理所有的IdleHandler)来实现的(并且会延迟5秒)。

    Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
          @Override public boolean queueIdle() {
            postToBackgroundWithDelay(retryable, failedAttempts);
            return false;
          }
        });
    

    检查
    在refWatcher.watch函数中,Activity被包装为一个KeyedWeakReference对象,KeyedWeakReference会在工作线程中监测引用是否被清除。
    KeyedWeakReference有key和referenceQueue。
    其中key是一个UID,用来定位最终泄露的对象,在retainedKeys(一个CopyOnWriteArraySet)里会add这个key,如果retainedKeys里不再有对应的key,就说明对象已经被回收了;
    referenceQueue用来监控弱引用的回收,这里应用了JVM中回收时虚引用和ReferenceQueue关联的原理,当GC回收一个对象时,如果发现它还有弱引用,就会在回收对象内存前,把这个对象加入到与弱引用关联的ReferenceQueue中,所以,如果一个对象出现在了相关联的ReferenceQueue中,就说明它即将被GC回收。
    所以,检查对象是否被清除,就通过相关联的referenceQueue做检查,如果queue中能pull出这个对象,就说明可以回收,清除掉retainedKeys里的key记录即可

    //给对象关联ReferenceQueue,以便判断回收
    final KeyedWeakReference reference =
            new KeyedWeakReference(watchedReference, key, referenceName, queue);
    ...
    while ((ref = (KeyedWeakReference) queue.poll()) != null) {//检查关联的referenceQueue
          retainedKeys.remove(ref.key);//清除key,只要没有这个key,就说明已清除
        }
    

    如果未清除,手动调用GC,如果还未清除,就有可能是内存泄露了,把内存dump到.hprof文件,用HeapAnalyzer去分析
    3.分析内存泄露
    在精细分析中实际上引用了开源项目HAHA
    先把.hprof转成Snapshot,拿到对象到GCRoot的所有引用路径,并剔除重复路径
    再找到泄露对象:
    ClassObj refClass = snapshot.findClass(KeyedWeakReference.class.getName());//找到泄露的类
    for (Instance instance : refClass.getInstancesList()) {...//遍历类的实例
    List<ClassInstance.FieldValue> values = classInstanceValues(instance);
    String keyCandidate = asString(fieldValue(values, "key"));//根据KeyedWeakReference对象中的key值,找到泄露对象
    最后找出最短引用路径:
    ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef);//拿最短引用路径
    LeakTrace leakTrace = buildLeakTrace(result.leakingNode);//生成LeakTrace
    4.输出
    最开始初始化,LeakCanary.intall(application)时,实际上注册了处理方式:
    public static RefWatcher install(Application application) {
    return refWatcher(application)
    .listenerServiceClass(DisplayLeakService.class)//处理方法
    .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())//排除部分泄露范围
    .buildAndInstall();
    }
    所以,是在DisplayLeakService.class中处理泄露的,我们可以自己扩展,比如改成发邮件什么的。
    5.其他
    为了确保上线安全,通过设定debugCompile,实现仅在debug中使用LeakCanary。

    引用

    Leakcanary
    LeakCanary核心原理源码浅析
    Java对象的强、软、弱和虚引用原理+结合ReferenceQueue对象构造Java对象的高速缓存器

    相关文章

      网友评论

          本文标题:[笔记]LeakCanary实现原理概述

          本文链接:https://www.haomeiwen.com/subject/ylawyxtx.html