leakcanary在android8.0上的小实验

leakcanary小实验

调研leakcanary参考链接如下:
https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/
https://github.com/square/leakcanary
leakcanary是一个android内存泄漏监测工具,原理利用了弱引用队列。工作机制如下:
工作流程
1.RefWatcher.watch() 创建一个 KeyedWeakReference 到要被监控的对象。
2.然后在后台线程检查引用是否被清除,如果没有,调用GC。
3.如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个 .hprof 文件中。
4.在另外一个进程中的 HeapAnalyzerService 有一个 HeapAnalyzer 使用HAHA 解析这个文件。
5.得益于唯一的 reference key, HeapAnalyzer 找到KeyedWeakReference,定位内存泄露。
6.HeapAnalyzer 计算 到 GC roots的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链。
7.引用链传递到 APP 进程中的 DisplayLeakService, 并以通知的形式展示出来。

在使用过程中配置你的build.gradle(注意是你的app的不是全局的)

dependencies{
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'
    releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.3'
}

实验代码如下:
静态对象引发的内存泄漏(自定义对象的监测,注意要自己继承一下application)

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.squareup.leakcanary.*;

class Cat {
    String name;
    Cat(String _name){
        name=_name;
    }
}
class Box {
    Cat hiddenCat;
}
class schrodinger{
    static Box hisbox;
}
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Box box = new Box();
        Cat schrodingerCat = new Cat("caffe");
        box.hiddenCat = schrodingerCat;
        schrodinger.hisbox = box;
        RefWatcher ref = myapplication.getRefWatcher(this);
        ref.watch(schrodingerCat);
    }
}

leakcanary中部分代码分析:
watch方法:

@Synchronized fun watch(
    watchedReference: Any,
    referenceName: String
  ) {
    removeWeaklyReachableReferences()
    //移除弱引用
    val key = UUID.randomUUID()
        .toString()
    //新观察对象的唯一的key
    val watchUptimeMillis = clock.uptimeMillis()
    //当前时间
    val reference =
      KeyedWeakReference(watchedReference, key, referenceName, watchUptimeMillis, queue)
    //建立弱引用
    if (referenceName != "") {
      CanaryLog.d(
          "Watching instance of %s named %s with key %s", reference.className,
          referenceName, key
      )
    } else {
      CanaryLog.d(
          "Watching instance of %s with key %s", reference.className, key
      )
    }
    watchedReferences[key] = reference
    //一个 map 可以用 key 关联到 弱引用
    checkRetainedExecutor.execute {
      moveToRetained(key)
    }
  }

泄漏监测:

fun detectLeaks(): Result {
    val leakDetectionTime = SystemClock.uptimeMillis()
    val watchDurationMillis = LeakSentry.config.watchDurationMillis
    val instrumentation = getInstrumentation()
    val context = instrumentation.targetContext
    val refWatcher = LeakSentry.refWatcher
    if (!refWatcher.hasWatchedReferences) {
      return NoAnalysis
    }
    instrumentation.waitForIdleSync()
    if (!refWatcher.hasWatchedReferences) {
      return NoAnalysis
    }
    runGc()//第一次GC
    if (!refWatcher.hasWatchedReferences) {
      return NoAnalysis
    }
    // Waiting for any delayed UI post (e.g. scroll) to clear. This shouldn't be needed, but
    // Android simply has way too many delayed posts that aren't canceled when views are detached.
    SystemClock.sleep(2000)
    if (!refWatcher.hasWatchedReferences) {
      return NoAnalysis
    }
    // Aaand we wait some more.
    // 4 seconds (2+2) is greater than the 3 seconds delay for
    // FINISH_TOKEN in android.widget.Filter
    SystemClock.sleep(2000)
    val endOfWatchDelay = watchDurationMillis - (SystemClock.uptimeMillis() - leakDetectionTime)
    if (endOfWatchDelay > 0) {
      SystemClock.sleep(endOfWatchDelay)
    }
    runGc()//第二次GC
    if (!refWatcher.hasRetainedReferences) {
      return NoAnalysis
    }
    //开始dump...后续省略
}
posted @ 2019-04-16 21:33  zhangxianlong  阅读(266)  评论(0编辑  收藏  举报