美文网首页
LeakCanary 源码解析

LeakCanary 源码解析

作者: 01_小小鱼_01 | 来源:发表于2019-06-11 19:35 被阅读0次

    一、 前言

    1. Java 内存模型
    image.png 运行时数据区域
    名称 特征 作用 配置参数 异常
    程序计数器 占用内存小,线程私有,生命周期与线程相同 大致为字节码行号指示器
    虚拟机栈 线程私有,生命周期与线程相同,使用连续的内存空间 Java 方法执行的内存模型,存储局部变量表、操作栈、动态链接、方法出口等信息 -Xss StackOverflowError、OutOfMemoryError
    java堆 线程共享,生命周期与虚拟机相同,可以不使用连续的内存地址 保存对象实例,所有对象实例(包括数组)都要在堆上分配 -Xms、-Xsx、-Xmn OutOfMemoryError
    方法区 线程共享,生命周期与虚拟机相同,可以不使用连续的内存地址 存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据 -XX:PermSize:16M 、-XX:MaxPermSize64M OutOfMemoryError
    运行时常量池 方法区的一部分,具有动态性 存放字面量及符号引用
    2. Java垃圾回收策略
    • 引用计数算法:给对象中添加一个引用计数器,每当有引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用。
    • 可达性分析算法:通过一系列的称为"GC Root"的对象作为起点,从这些节点开始向下搜索,搜素所走过的路径称为引用链(Reference Chain),当一个对象到GC Root没有任何的引用链相连,就判定对象可以被回收
    对象可达性
    3. Java 四种引用类型
    • 强引用:默认的引用方式,当内存空间不足,JVM宁愿触发OOM,也不会对这部分内存进行回收。
    Object obj = new Object();
    obj = null;
    
    • 软引用(SoftReference):当内存空间不足的时候,JVM会回收这部分内存,一般用来做缓存策略。
    • 弱引用(WeakReference): 当GC回收的时候,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收这部分内存。
    • 虚引用(PhantomReference):当 GC回收的时候会回收这部分内存, 主要用于检测对象是否已经从内存中删除。
    代码示例
    public class TestReference {
    
        public static void main(String[] args) throws InterruptedException {
            ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
            String sw = "虚引用";
    
            switch (sw) {
                case "软引用":
                    Object objSoft = new Object();
                    SoftReference<Object> softReference = new SoftReference<>(objSoft, referenceQueue);
                    System.out.println("GC前获取:" + softReference.get());
                    objSoft = null;
                    System.gc();
                    Thread.sleep(1000);
                    System.out.println("GC后获取:" + softReference.get());
                    System.out.println("队列中的结果:" + referenceQueue.poll());
                    break;
                case "弱引用":
                    Object objWeak = new Object();
                    WeakReference<Object> weakReference = new WeakReference<>(objWeak, referenceQueue);
                    System.out.println("GC前获取:" + weakReference.get());
                    objWeak = null;
                    System.gc();
                    Thread.sleep(1000);
                    System.out.println("GC后获取:" + weakReference.get());
                    System.out.println("队列中的结果:" + referenceQueue.poll());
                    break;
                case "虚引用":
                    Object objPhan = new Object();
                    PhantomReference<Object> phantomReference = new PhantomReference<>(objPhan, referenceQueue);
                    System.out.println("GC前获取:" + phantomReference.get());
                    objPhan = null;
                    System.gc();
                    //此处的区别是当objPhan的内存被gc回收之前虚引用就会被加入到ReferenceQueue队列中,其他的引用都为当引用被gc掉时候,引用会加入到ReferenceQueue中
                    Thread.sleep(1000);
                    System.out.println("GC后获取:" + phantomReference.get());
                    System.out.println("队列中的结果:" + referenceQueue.poll());
                    break;
            }
        }
    }
    

    软引用运行结果:

    软引用运行结果

    弱引用运行结果:

    弱引用运行结果

    虚引用运行结果:

    虚引用运行结果

    二、 集成

    build.gradle文件

    dependencies {
       // debug 版本依赖
      debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.2'
      // release 版本依赖  
      releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2'
      // 如果使用了 support fragment,请同时依赖
      debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.2'
    
      // 最新2.0版本,只需要导入如下依赖,不再需要在Application里面添加其他代码
      // debugImplementation because LeakCanary should only run in debug builds.
      // debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.0-alpha-2'
    }
    

    Application#onCreate方法

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

    三、 LeakCanary源码导读

    3.1. LeakCanary项目架构

    LeakCanary 项目结构图:

    LeakCanary项目结构
    • leakcanary-analyzer : 负责分析内存泄漏
    • leakcanary-android : android 核心模块以及通知、界面展示等
    • leakcanary-android-instrumentation : 单元测试使用
    • leakcanary-sample : 使用案例
    • leakcanary-support-fragment : 支持 fragment v4包
    • leakcanary-watcher : 负责监听内存泄漏
    • leakcanary-android-no-op : release 版本使用
    3.2. LeakCanary执行过程
    LeakCanary执行过程
    3.3. LeakCanary初始化
      /**
       * Whether the current process is the process running the {@link HeapAnalyzerService}, which is
       * a different process than the normal app process.
       */
      public static boolean isInAnalyzerProcess(@NonNull Context context) {
        Boolean isInAnalyzerProcess = LeakCanaryInternals.isInAnalyzerProcess;
        // This only needs to be computed once per process.
        Log.d("LeakCanary", "isInAnalyzerProcess = " + isInAnalyzerProcess);
        if (isInAnalyzerProcess == null) {
          // HeapAnalyzerService进程名是否等于主进程名称
          isInAnalyzerProcess = isInServiceProcess(context, HeapAnalyzerService.class);
          LeakCanaryInternals.isInAnalyzerProcess = isInAnalyzerProcess;
        }
        return isInAnalyzerProcess;
      }
    
      public static @NonNull void install(@NonNull Application application) {
        refWatcher(application)
            .listenerServiceClass(DisplayLeakService.class)  // 设置 heapDumpListener
            .excludedRefs(AndroidExcludedRefs.createAppDefaults().build()) // 去除Android SDK 引起的内存泄漏
            .buildAndInstall();  //创建RefWatcher
      }
    
      public static @NonNull AndroidRefWatcherBuilder refWatcher(@NonNull Context context) {
        return new AndroidRefWatcherBuilder(context);
      }
    

    isInAnalyzerProcess方法,用于检测内存分析进程是否和主进程名相同,如果是相同的,则设置RefWatcher = DISABLED ,不做任何处理。release 版本是通过以下以来确定的。
    // release 版本依赖
    releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2'

    3.4 AndroidRefWatcherBuilder 对象
      public @NonNull AndroidRefWatcherBuilder listenerServiceClass(
          @NonNull Class<? extends AbstractAnalysisResultService> listenerServiceClass) {
        // isAssignableFrom()方法是判断是否为某个类的父类,instanceof()方法是判断是否某个类的子类。
        //(https://www.cnblogs.com/bethunebtj/p/4681438.html)
        enableDisplayLeakActivity = DisplayLeakService.class.isAssignableFrom(listenerServiceClass);
        return heapDumpListener(new ServiceHeapDumpListener(context, listenerServiceClass));
      }
    
      public final T heapDumpListener(HeapDump.Listener heapDumpListener) {
          this.heapDumpListener = heapDumpListener;
          return self();
      }
    
      public final T excludedRefs(ExcludedRefs excludedRefs) {
        heapDumpBuilder.excludedRefs(excludedRefs);
        return self();
      }
    
      @Override protected @NonNull HeapDumper defaultHeapDumper() {
        LeakDirectoryProvider leakDirectoryProvider =
            LeakCanaryInternals.getLeakDirectoryProvider(context);
        return new AndroidHeapDumper(context, leakDirectoryProvider);
      }
    
      public @NonNull RefWatcher buildAndInstall() {
        if (LeakCanaryInternals.installedRefWatcher != null) {
          throw new UnsupportedOperationException("buildAndInstall() should only be called once.");
        }
        RefWatcher refWatcher = build();
        if (refWatcher != DISABLED) {
          Log.d("LeakCanary", "enableDisplayLeakActivity = " + enableDisplayLeakActivity);
          if (enableDisplayLeakActivity) {
            LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
          }
          Log.d("LeakCanary", "watchActivities = " + watchActivities);
          if (watchActivities) {
            ActivityRefWatcher.install(context, refWatcher);
          }
          Log.d("LeakCanary", "watchFragments = " + watchFragments);
          if (watchFragments) {
            FragmentRefWatcher.Helper.install(context, refWatcher);
          }
        }
        LeakCanaryInternals.installedRefWatcher = refWatcher;
        return refWatcher;
      }
    
      public final RefWatcher build() {
          if (isDisabled()) {
              return RefWatcher.DISABLED; 
          }
          if (heapDumpBuilder.excludedRefs == null) {
              heapDumpBuilder.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();
          }
          if (heapDumpBuilder.reachabilityInspectorClasses == null) {
              heapDumpBuilder.reachabilityInspectorClasses(defaultReachabilityInspectorClasses());
          }
          return new RefWatcher(watchExecutor, debuggerControl, gcTrigger
                                , heapDumper, heapDumpListener, heapDumpBuilder);
      }
    
      @Override protected @NonNull WatchExecutor defaultWatchExecutor() {
        return new AndroidWatchExecutor(DEFAULT_WATCH_DELAY_MILLIS);
      }
    

    buildAndInstall方法则用于设置heapDumpListener用于分析hprof文件, 过滤掉因为Android SDK引起的系统内存泄漏引用, 展示DisplayLeakActivity桌面icon,监听Activity 以及Fragment生命周期,在其销毁的时候调用RefWatcher的watch方法分析是否会发生内存泄漏对象。build方法则是初始化RefWatcher对象,

    关于设置DisplayLeakActivity enabled = true的地方,需要看看详细的代码

        LeakCanaryInternals.setEnabledAsync(context, DisplayLeakActivity.class, true);
    
        public static void setEnabledAsync(Context context, final Class<?> componentClass,
                                           final boolean enabled) {
            final Context appContext = context.getApplicationContext();
            AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
                @Override
                public void run() {
                    setEnabledBlocking(appContext, componentClass, enabled);
                }
            });
        }
    
        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);
        }
    

    其实就是利用AsyncTask的THREAD_POOL_EXECUTOR线程池去执行PackageManager的setComponentEnabledSetting方法,动态设置COMPONENT_ENABLED_STATE_ENABLED


    image.png
    3.5 RefWatcher 对象
      private final Set<String> retainedKeys;
      private final ReferenceQueue<Object> queue; // 引用队列
    
      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);
      }
    
      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);
    
        // 移除所有弱引用可达对象
        removeWeaklyReachableReferences();
    
        // 如果VM正连接到Debuger,忽略这次检测,因为Debugger可能会持有一些在当前上下文中不可见的对象,导致误判
        if (debuggerControl.isDebuggerAttached()) {
          // The debugger can create false leaks.
          return RETRY;
        }
        //上面执行 removeWeaklyReachableReferences 方法,判断是不是监视对象已经被回收了,如果被回收了,那么说明没有发生内存泄漏,直接结束
        if (gone(reference)) {
          return DONE;
        }
         // 手动触发一次 GC 垃圾回收
        gcTrigger.runGc();
        // 再次移除所有弱引用可达对象
        removeWeaklyReachableReferences();
        if (!gone(reference)) {
          long startDumpHeap = System.nanoTime();
          long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
          // 利用Debug生成Hprof文件
          File heapDumpFile = heapDumper.dumpHeap();
          if (heapDumpFile == RETRY_LATER) {
            // Could not dump the heap.
            return RETRY;
          }
          long heapDumpDurationMs = NANOSECONDS.toMillis(System.nanoTime() - startDumpHeap);
    
          HeapDump heapDump = heapDumpBuilder.heapDumpFile(heapDumpFile).referenceKey(reference.key)
              .referenceName(reference.name)
              .watchDurationMs(watchDurationMs)
              .gcDurationMs(gcDurationMs)
              .heapDumpDurationMs(heapDumpDurationMs)
              .build();
    
          heapdumpListener.analyze(heapDump);  // 开始分析hprof文件
        }
        return DONE;
      }
    
      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);
        }
      }
    

    RefWatcher很重要的方法就是watch方法, 其中参数watchedReference就是对应的view、activity、fragment对象。 通过UUID 函数生成唯一的标识添加到Set<String>集合当中。 KeyedWeakReference继承了WeakReference,关联了watchedReference。当进弱引用对象发生回收的时候,虚拟机就会向该引用添加到与之关联的引用队列当中。

    检测内存是否泄漏的过程也很简单:首先会清除所有的弱引用可达对象, 判断watchedReference对象是否已经被回收了, 如果没有,则手动进行一个GC,二次确认watchedReference对象是否被回收,如果没被回收则dump hprof文件,通知heapdumpListener分析hprof文件

    3.6. AndroidWatchExecutor 类
    public final class AndroidWatchExecutor implements WatchExecutor {
    
     static final String LEAK_CANARY_THREAD_NAME = "LeakCanary-Heap-Dump";
     private final Handler mainHandler;
     private final Handler backgroundHandler;
     private final long initialDelayMillis;
     private final long maxBackoffFactor;
    
     public AndroidWatchExecutor(long initialDelayMillis) {
       mainHandler = new Handler(Looper.getMainLooper());
       HandlerThread handlerThread = new HandlerThread(LEAK_CANARY_THREAD_NAME);
       handlerThread.start();
       backgroundHandler = new Handler(handlerThread.getLooper());
       this.initialDelayMillis = initialDelayMillis;
       maxBackoffFactor = Long.MAX_VALUE / initialDelayMillis;
     }
    
     @Override public void execute(@NonNull Retryable retryable) {
       if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
         waitForIdle(retryable, 0);
       } else {
         postWaitForIdle(retryable, 0);
       }
     }
    
     private void postWaitForIdle(final Retryable retryable, final int failedAttempts) {
       mainHandler.post(new Runnable() {
         @Override public void run() {
           waitForIdle(retryable, failedAttempts);
         }
       });
     }
    
     private void waitForIdle(final Retryable retryable, final int failedAttempts) {
       // This needs to be called from the main thread.
       Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
         @Override public boolean queueIdle() {
           postToBackgroundWithDelay(retryable, failedAttempts);
           return false;
         }
       });
     }
    
     private void postToBackgroundWithDelay(final Retryable retryable, final int failedAttempts) {
       long exponentialBackoffFactor = (long) Math.min(Math.pow(2, failedAttempts), maxBackoffFactor);
       long delayMillis = initialDelayMillis * exponentialBackoffFactor;
       // 利用HandlerThread对象里面的Looper进行子线程任务
       backgroundHandler.postDelayed(new Runnable() {
         @Override public void run() {
           Retryable.Result result = retryable.run();
           if (result == RETRY) {
             postWaitForIdle(retryable, failedAttempts + 1);
           }
         }
       }, delayMillis);
     }
    }
    

    WatchExecutor 是一个线程调度器。包含了一个主线程 mainHandler 和后台线程backgroundHandler。整个的逻辑就是当主线程空闲的时候,才回去启动后台线程去执行Retryable.run方法。

    3.7. AndroidHeapDumper 生成hprof文件
      public File dumpHeap() {
        // 生成hprof文件
        File heapDumpFile = leakDirectoryProvider.newHeapDumpFile();
    
        if (heapDumpFile == RETRY_LATER) {
          return RETRY_LATER;
        }
    
        FutureResult<Toast> waitingForToast = new FutureResult<>();
        showToast(waitingForToast);
    
        if (!waitingForToast.wait(5, SECONDS)) {
          Log.d("LeakCanary", "Did not dump heap, too much time waiting for Toast.");
          CanaryLog.d("Did not dump heap, too much time waiting for Toast.");
          return RETRY_LATER;
        }
        // 创建正在dumping通知
        Notification.Builder builder = new Notification.Builder(context)
            .setContentTitle(context.getString(R.string.leak_canary_notification_dumping));
        Notification notification = LeakCanaryInternals.buildNotification(context, builder);
        NotificationManager notificationManager =
            (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        int notificationId = (int) SystemClock.uptimeMillis();
        notificationManager.notify(notificationId, notification);
    
        Toast toast = waitingForToast.get();
        try {
          // 系统Debug类提供的方法
          Debug.dumpHprofData(heapDumpFile.getAbsolutePath());
          cancelToast(toast);
          notificationManager.cancel(notificationId);
          return heapDumpFile;
        } catch (Exception e) {
          CanaryLog.d(e, "Could not dump heap");
          // Abort heap dump
          return RETRY_LATER;
        }
      }
    
      private void showToast(final FutureResult<Toast> waitingForToast) {
        mainHandler.post(new Runnable() {
          @Override public void run() {
            if (resumedActivity == null) {
              waitingForToast.set(null);
              return;
            }
            final Toast toast = new Toast(resumedActivity);
            toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
            toast.setDuration(Toast.LENGTH_LONG);
            LayoutInflater inflater = LayoutInflater.from(resumedActivity);
            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);
                Log.d("LeakCanary", "latch.countDown()");
                return false;
              }
            });
          }
        });
      }
    

    dumpHeap方法利用Debug.dumpHprofData(String filePath)生成hprof文件,同时会展示Toast以及通知栏状态。如果dump失败或者Toast展示时间太长就会返回RETRY_LATER。

    3.8 HeapAnalyzerService 分析hprof文件,找出泄漏路径
      @Override protected void onHandleIntentInForeground(@Nullable 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, this, heapDump.reachabilityInspectorClasses);
    
        Log.d("LeakCanary", "leak path:" + heapDump.heapDumpFile.getAbsolutePath());
    
        AnalysisResult result = heapAnalyzer.checkForLeak(heapDump.heapDumpFile, heapDump.referenceKey,
            heapDump.computeRetainedHeapSize);
        AbstractAnalysisResultService.sendResultToListener(this, listenerClassName, heapDump, result);
      }
    

    HeapAnalyzer 是hprof文件解析类, 使用了squareup的另外一个开源库—Haha。

      public @NonNull AnalysisResult checkForLeak(@NonNull File heapDumpFile,
          @NonNull String referenceKey,
          boolean computeRetainedSize) {
        long analysisStartNanoTime = System.nanoTime();
    
        Log.d("LeakCanary", "heapDumpFile path:" + heapDumpFile.getAbsolutePath());
        Log.d("LeakCanary", "referenceKey = " + referenceKey);
        if (!heapDumpFile.exists()) {
          Exception exception = new IllegalArgumentException("File does not exist: " + heapDumpFile);
          return failure(exception, since(analysisStartNanoTime));
        }
    
        // Hprof 文件协议介绍: https://my.oschina.net/u/217380/blog/1507542
        try {
          listener.onProgressUpdate(READING_HEAP_DUMP_FILE);
          // 把hprof文件映射到内存 ByteBuffer[]
          HprofBuffer buffer = new MemoryMappedFileBuffer(heapDumpFile);
          HprofParser parser = new HprofParser(buffer);
          listener.onProgressUpdate(PARSING_HEAP_DUMP);
          // 创建了HprofParser对象,parse方法解析hprof协议,生成Snapshot
          Snapshot snapshot = parser.parse();
          listener.onProgressUpdate(DEDUPLICATING_GC_ROOTS);
          // 去除重复的GC root对象
          deduplicateGcRoots(snapshot);
          listener.onProgressUpdate(FINDING_LEAKING_REF);
          Instance leakingRef = findLeakingReference(referenceKey, snapshot);
    
          // 此对象不存在,表示已经被gc清除了,不存在泄露因此返回无泄漏
          // False alarm, weak reference was cleared in between key check and heap dump.
          if (leakingRef == null) {
            String className = leakingRef.getClassObj().getClassName();
            return noLeak(className, since(analysisStartNanoTime));
          }
          // 此对象存在, 也不能确认它内存泄漏了,要检测此对象的gc root
          return findLeakTrace(analysisStartNanoTime, snapshot, leakingRef, computeRetainedSize);
        } catch (Throwable e) {
          return failure(e, since(analysisStartNanoTime));
        }
      }
    
    image.png
    3.9 DisplayLeakService 生成内存泄漏通知栏
      protected final void onHeapAnalyzed(@NonNull AnalyzedHeap analyzedHeap) {
        Log.d("LeakCanary", "[DisplayLeakService]: onHeapAnalyzed");
        HeapDump heapDump = analyzedHeap.heapDump;
        AnalysisResult result = analyzedHeap.result;
    
        String leakInfo = leakInfo(this, heapDump, result, true);
        CanaryLog.d("%s", leakInfo);
    
        heapDump = renameHeapdump(heapDump);
        boolean resultSaved = saveResult(heapDump, result);
    
        String contentTitle;
        if (resultSaved) {
          PendingIntent pendingIntent =
              DisplayLeakActivity.createPendingIntent(this, heapDump.referenceKey);
          if (result.failure != null) {
            contentTitle = "Leak analysis failed";
          } else {
            String className = classSimpleName(result.className);
            if (result.leakFound) {
              if (result.retainedHeapSize == AnalysisResult.RETAINED_HEAP_SKIPPED) {
                if (result.excludedLeak) {
                  contentTitle = getString(R.string.leak_canary_leak_excluded, className);
                } else {
                  contentTitle = getString(R.string.leak_canary_class_has_leaked, className);
                }
              } else {
                String size = formatShortFileSize(this, result.retainedHeapSize);
                if (result.excludedLeak) {
                  contentTitle =
                      getString(R.string.leak_canary_leak_excluded_retaining, className, size);
                } else {
                  contentTitle =
                      getString(R.string.leak_canary_class_has_leaked_retaining, className, size);
                }
              }
            } else {
              contentTitle = getString(R.string.leak_canary_class_no_leak, className);
            }
          }
          String contentText = getString(R.string.leak_canary_notification_message);
          showNotification(pendingIntent, contentTitle, contentText);
        } else {
          onAnalysisResultFailure(getString(R.string.leak_canary_could_not_save_text));
        }
    
        afterDefaultHandling(heapDump, result, leakInfo);
      }
    
    四、LeakCanary不足

    虽然 LeakCanary 有诸多优点,但是它也有做不到的地方,比如说检测申请大容量内存导致的OOM问题、Bitmap内存未释放问题,Service 中的内存泄漏可能无法检测等。

    五、延申阅读

    1. Matrix ResourceCanary -- Activity 泄漏及Bitmap冗余检测
    2. Hprof 文件协议
    3. GC那些事儿--Android内存优化第一弹

    相关文章

      网友评论

          本文标题:LeakCanary 源码解析

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