Android内存泄漏检测工具:LeakCanary

453 阅读3分钟

一、简介

LeakCanary是一个傻瓜式并且可视化内存泄漏分析工具


二、使用方法

尽量在app下的build.grade中加入以下依赖

 dependencies {
   debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' // or 1.4-beta1
   releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1
   testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1
 }

在Application中加入类似如下的代码

public class ExampleApplication extends Application {
  @Override public void onCreate() {
    super.onCreate();
    LeakCanary.install(this);
  }
}


此时,你就可检测到Activity的内存泄漏 。其实现原理是设置Application的ActivityLifycycleCallbacks()方法监控所有Activity的生命周期回调。内部实现代码为:

public final class ActivityRefWatcher {
    private final ActivityLifecycleCallbacks lifecycleCallbacks = 
                   new ActivityLifecycleCallbacks() {
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        }

        public void onActivityStarted(Activity activity) {
        }

        public void onActivityResumed(Activity activity) {
        }

        public void onActivityPaused(Activity activity) {
        }

        public void onActivityStopped(Activity activity) {
        }

        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
        }

        public void onActivityDestroyed(Activity activity) {
            ActivityRefWatcher.this.onActivityDestroyed(activity);
        }
    };
    private final Application application;
    private final RefWatcher refWatcher;

    public static void installOnIcsPlus(Application application, RefWatcher refWatcher) {
        if(VERSION.SDK_INT >= 14) {
            ActivityRefWatcher activityRefWatcher =
                        new ActivityRefWatcher(application, refWatcher);
            activityRefWatcher.watchActivities();
        }
    }
....
}


三、检测更多的内存泄漏

首先我们需要获得一个RefWatcher,用来后续监控可能发现泄漏的对象

public class MyApplication extends Application {
    private static RefWatcher sRefWatcher;

    @Override
    public void onCreate() {
        super.onCreate();
        sRefWatcher = LeakCanary.install(this);
    }

    public static RefWatcher getRefWatcher() {
        return sRefWatcher;
    }
}

监控某个可能存在内存泄漏的对象

MyApplication.getRefWatcher().watch(sLeaky);


四、需要进行监控的对象

默认情况下,是对Activity进行了检测。另一个需要监控的重要对象就是Fragment实例。因为它和Activity实例一样可能持有大量的视图以及视图需要的资源(如Bitmap),即在Fragment.onDestroy()方法中加入如下实现监控:

public class MainFragment extends Fragment {
    @Override
    public void onDestroy() {
        super.onDestroy();
        MyApplication.getRefWatcher().watch(this);
    }
}


其他也可以监控的对象

  • BroadcastReceiver
  • Service
  • 其他有生命周期的对象
  • 直接或间接持有大内存占用的对象(即Retained Heap值比较大的对象)


五、何时进行监控

内存泄漏,简而言之,就是某个对象在该释放的时候由于被其他对象持有,而不能被释放,从而造成内存泄漏。

因此,监控需要设置在对象(很快)被释放的时候,如ActivityFragmentonDestroy()方法。

注意,切不可在初始化时!!!

如监控一个Activity,放在onCreate()就会大错特错了,因为这样每次都会收到Activity的泄漏通知。


六、内存泄漏解决方法

常见的解决方法思路:

  • 尽量使用ApplicationContext而不是Activity的
  • 使用弱引用或软引用
  • 手动设置null解除引用关系
  • 内部类设置为static不隐式持有外部的实例
  • 注册与反注册成对出现,在对象何时的生命周期进行反注册操作
  • 如果没有修改的权限,比如系统或则第三方SDK,那么使用反射进行解决持有关系


七、加入例外

有些特殊情况,我们需要忽略一些问题,这时候就需要添加例外规则。比如ExampleClass.exampleField(某类的某个成员变量)会导致内存泄漏,我们想要忽略,如下操作即可:

// ExampleApplication is defined in "Customizing and using the no-op dependency"
public class DebugExampleApplication extends ExampleApplication {
  protected RefWatcher installLeakCanary() {
    ExcludedRefs excludedRefs = AndroidExcludedRefs.createAppDefaults()
        .instanceField("com.example.ExampleClass", "exampleField")
        .build();
    return LeakCanary.install(this, DisplayLeakService.class, excludedRefs);
  }
}


八、如何实现

LeakCanary实际上就是在本机上自动做了Heap dump,然后对生成的hprof文件分析,进行结果展示。和手动进行MAT分析步骤基本一致。


九、如何不影响对外版APK

因为LeakCanary确实是影响程序运行的,尤其是heap dump操作,不过好在这件事Square已经考虑了,即增加依赖时

debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' // or 1.4-beta1
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' // or 1.4-beta1

其中releaseCompiletestCompile这两个的依赖明显不同于debugCompile的依赖。它们的依赖属于NOOP操作

NOOP,即No Operation Performed无操作指令。常见的编译器技术会检测无操作指令并出于优化的目的将无操作指令剔除

因而,只要配置好releaseCompile和testCompile的依赖,就无需担心对外版本的性能问题了。


内容来源:Android内存泄漏检测利器:LeakCanary