LeakCanary内存泄漏分析利器

2019-03-12  本文已影响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. 确定内存是否泄漏

3. dump hprof

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

4. 分析内存泄漏位置

LeakCanary面试题

1. Activity检测机制是什么?

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

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

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

上一篇 下一篇

猜你喜欢

热点阅读