美文网首页
LeakCanary内存泄漏分析利器

LeakCanary内存泄漏分析利器

作者: 小的橘子 | 来源:发表于2019-03-12 23:25 被阅读0次

    LeakCanary github地址 https://github.com/square/leakcanary

    LeakCanary分析实例

    1. Activity内存泄漏示例

    LeakApplication.java

    public class LeakApplication extends Application {
        private RefWatcher refWatcher;
    
        @Override
        public void onCreate() {
            super.onCreate();
            // 1. 判断是否是LeakCanary所在进程
            if (LeakCanary.isInAnalyzerProcess(this)) {
                return;
            }
            // 2. 核心代码
            LeakCanary.install(this);
        }
    }
    

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            // 1. 该线程为成员内部类,其包含MainActivity实例
            new MyThread().start();
        }
     
        class MyThread extends Thread {
            @Override
            public void run() {
                super.run();
                try {
                    // 2. 延时5分钟
                    TimeUnit.MINUTES.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    启动界面后返回退出即出现内存泄漏,点击LeakCanary发出的通知,如下图


    Activity内存泄漏

    图中标红色波浪线的为可能导致内存泄漏对象,此题通过this$0即可判断是子线程持有MainActivity引用,Activity退出后理应被GC回收,但由于子线程持有其引用导致无法释放,从而内存泄漏。

    2. Service内存泄漏示例

    LeakCanary默认会获取Activity的内存泄漏,对于检测Service需要手动在Service的onDestory()中进行监测,代码如下:
    LeakApplication

    public class LeakApplication extends Application {
        // 1. RefWatcher用来监测目标对象
        private RefWatcher refWatcher;
    
        @Override
        public void onCreate() {
            super.onCreate();
            refWatcher = setupLeakCanary();
        }
        // 2. 返回RefWatcher对象
        private RefWatcher setupLeakCanary() {
            if (LeakCanary.isInAnalyzerProcess(this)) {
                return RefWatcher.DISABLED;
            }
            return LeakCanary.install(this);
        }
        // 3. 给整个应用提供获取接口
        public static RefWatcher getRefWatcher(Context context) {
            LeakApplication leakApplication = (LeakApplication) context.getApplicationContext();
            return leakApplication.refWatcher;
        }
    }
    
    

    MyService

    public class MyService extends Service {
        public static final String TAG = "wangyannan";
        // 1. 静态变量
        private static Context sContext;
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i(TAG, "onStartCommand: ");
            // 2. 当前Service对象赋值给静态变量context
            sContext = this;
            return super.onStartCommand(intent, flags, startId);
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            // 3. 得到RefWatcher对象
            RefWatcher refWatcher = LeakApplication.getRefWatcher(this);
            // 4. 监听Service对象是否内存泄漏
            refWatcher.watch(this);
            Log.i(TAG, "onDestroy: ");
        }
    }
    

    在MainActivity中启动Service,然后停止Service,LeakCanary监测到内存泄漏,如下图


    Service内存泄漏

    红色波浪线中MyService.sContext比较明显,可见静态引用出现的问题,稍加判断就可以知道是包含了Service对象而导致其没有释放,故出现内存泄漏。

    LeakCanary源码分析 绝对简单易懂

    能检测Activity内存泄漏主要就是自定义Application中的该方法调用
    MyApplication.java

    LeakCanary.install(this);
    

    LeakCanary.java

    public static @NonNull RefWatcher install(@NonNull Application application) {
        return refWatcher(application).listenerServiceClass(DisplayLeakService.class)
            .excludedRefs(AndroidExcludedRefs.createAppDefaults().build())
            .buildAndInstall();  // RefWatcher通过buildAndInstall()返回
    }
    

    AndroidRefWatcherBuilder.java

    public @NonNull RefWatcher buildAndInstall() {
        ...
        RefWatcher refWatcher = build();
        if (refWatcher != DISABLED) {
          if (watchActivities) {
             // 1. 用于Activity的onDestory检测
            ActivityRefWatcher.install(context, refWatcher);
          }
          if (watchFragments) {
            // 2. 用于Fragment的onDestory检测
            FragmentRefWatcher.Helper.install(context, refWatcher);
          }
        }
        LeakCanaryInternals.installedRefWatcher = refWatcher;
        return refWatcher;
    }
    

    这里看Activity的onDestory是如何被检测的
    ActivityRefWatcher.java

    public static void install(@NonNull Context context, @NonNull RefWatcher refWatcher) {
        Application application = (Application) context.getApplicationContext();
        ActivityRefWatcher activityRefWatcher = new ActivityRefWatcher(application, refWatcher);
        // 1. 
        application.registerActivityLifecycleCallbacks(activityRefWatcher.lifecycleCallbacks);
    }
    // 2.
    private final Application.ActivityLifecycleCallbacks lifecycleCallbacks =
        new ActivityLifecycleCallbacksAdapter() {
        @Override public void onActivityDestroyed(Activity activity) {
            // 3. RefWatcher监控activity是否内存泄漏
          refWatcher.watch(activity);
        }
    };
    

    注释1处会通过Application的registerActivityLifecycleCallbacks方法注册监听Activity的生命周期,参数会传入一个callback,当Activity对应生命周期执行时,Callback中与Activity生命周期对应的方法也会走。例如Activity执行onDestory()方法时,就会回调Callback中对应方法,从而执行了上面注释2的onActivityDestroyed方法.

    RefWatcher.java

    public void watch(Object watchedReference) {
        watch(watchedReference, "");
    }
    public void watch(Object watchedReference, String referenceName) {
        if (this == DISABLED) {
          return;
        }
        // 如果为NULL就抛出异常
        checkNotNull(watchedReference, "watchedReference");
        checkNotNull(referenceName, "referenceName");
        final long watchStartNanoTime = System.nanoTime();
        // 生成唯一的key值
        String key = UUID.randomUUID().toString();
        // 加入到retainedKeys集合
        retainedKeys.add(key);
        // 创建弱引用与activity关联,queue为ReferenceQueue
        final KeyedWeakReference reference =
            new KeyedWeakReference(watchedReference, key, referenceName, queue);
        // 异步执行,最终用来判断
        ensureGoneAsync(watchStartNanoTime, reference);
    }
    

    ensureGoneAsync最终会执行ensureGone方法

    RefWatcher.java

    Retryable.Result ensureGone(final KeyedWeakReference reference, final long watchStartNanoTime) {
        long gcStartNanoTime = System.nanoTime();
        long watchDurationMs = NANOSECONDS.toMillis(gcStartNanoTime - watchStartNanoTime);
        // 1. 尝试移除key(如果Activity被回收了,该key会从retainedKeys集合中被删除)
        removeWeaklyReachableReferences();
        // 2. 判断key是否已经被移除,移除返回true
        if (gone(reference)) {
          return DONE;
        }
        // 3. 没有移除则主动触发一次GC
        gcTrigger.runGc();
        // 4. 效果和注释1相同
        removeWeaklyReachableReferences();
        // 5. 如果依然key依然没有从retainedKeys集合中移除,则说明出现内存泄漏
        if (!gone(reference)) {
          long startDumpHeap = System.nanoTime();
          long gcDurationMs = NANOSECONDS.toMillis(startDumpHeap - gcStartNanoTime);
    
          // 6. dump 堆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();
          // 7. 分析出泄漏的地方
          heapdumpListener.analyze(heapDump);
        }
        return DONE;
    }
    private void removeWeaklyReachableReferences() {
        KeyedWeakReference ref;
        // queue.poll()有值则说明WeakReference中的Activity被回收了,如果Activity的强引用还存在,那么WeakReference包含的activity不会被回收,queue.poll还是null
        while ((ref = (KeyedWeakReference) queue.poll()) != null) {
          // 如果被回收,则移除
          retainedKeys.remove(ref.key);
        }
    }
    

    整体工作也就清除了。总结下过程如下。

    1. 监听Activity或者Fragment在onDestory方法中

    application.registerActivityLifecycleCallbacks 该方法可以监听Activity的onDestory方法

    2. 确定内存是否泄漏

    • Activity onDestory时,将Activity通过WeakReference引用并绑定一个通过UUID.randomUUID().toString()生成唯一的Key,该key交由一个Set集合维护,该WeakReference会传入一个ReferenceQueue。
    • Activity执行onDestory时默认会触发一次GC,那么WeakRerence中activity就会被回收,并放入ReferenceQueue。LeakCanary就是通过判断ReferenceQueue是否包含activity对象,有则强转为WeakRerference,再从Set集合中移除WeakRerfernce的key。
    • 如果key在Set集合中已不存在,表明该Activity被回收,如果两次GC,该key在Set中还没有被移除,则说明存在内存泄漏。

    3. dump hprof

    实质通过Debug.dumpHprofData() dump堆信息

    4. 分析内存泄漏位置

    LeakCanary面试题

    1. Activity检测机制是什么?

    通过application.registerActivityLifecycleCallbacks来绑定Activity生命周期的监听,从而监控所有Activity; 在Activity执行onDestroy时,开始检测当前页面是否存在内存泄漏,并分析结果。因此,如果想要在不同的地方都需要检测是否存在内存泄漏,需要手动添加。

    2. 内存泄漏检测机制是什么?

    KeyedWeakReference与ReferenceQueue联合使用,在弱引用关联的对象被回收后,会将引用添加到ReferenceQueue;清空后,可以根据是否继续含有该引用来判定是否被回收;判定回收, 手动GC, 再次判定回收,采用双重判定来确保当前引用是否被回收的状态正确性;如果两次都未回收,则确定为泄漏对象。

    相关文章

      网友评论

          本文标题:LeakCanary内存泄漏分析利器

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