• 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...后续省略
    }
    
  • 相关阅读:
    Unity5和WebGL移植指南的一些总结
    Unite洛杉矶峰会精彩回顾:从图形、平台再到VR
    比代码更重要的是团队管理
    初期游戏编程的9大法则
    关于Unity3D手机网游开发一些小看法
    守住真我
    学习要讲方法
    学习笔记|编程风格
    随便记
    微信方法
  • 原文地址:https://www.cnblogs.com/zhangxianlong/p/10720197.html
Copyright © 2020-2023  润新知