LeakCanary
项目地址:https://github.com/square/leakcanary
使用方法:
dependencies { debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' }
//no-op 是一个空壳,为了方便编译正式版本,这样不用改代码生成的正式版本上也不会影响性能。
代码注入:
public class ExampleApplication extends Application { @Override public void onCreate() { super.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); // Normal app init code... } }
LeakCanary.install 会返回一个 RefWatcher 利用 RefWatcher可以监视任意你怀疑的对象。 LeakCanary 会自动监视Activity对象。当Activity onDestory之后,LeakCanary会分析Activity的对象是否销毁。如果被销毁,那么就产生Activity对象泄露。
泄漏反馈:
LeakCanary 自带了一个界面来显示内存泄漏的对象列表,和对象被应用的情况,如下:我手机有个系统bug,为了截屏方便持有了Activity,导致Activity消失之后没有及时被回收。 原理:
LeakCanary原理是利用Java 弱引用(WeakReference)机制监察这个弱应用持有的对象是否为null来判断对象是否被GC。 如果发现对象有泄露就调用DumpHeap来查找泄露对象的引用情况,类似MAT。
使用说明:
对于任何Java对象来说,程序并不确定对象是否该被释放,所以才会有内存泄漏。LeakCanary也无法判断对象是否该被释放了,它只是利用了某些对象带生命周期,正常情况对象在生命周期结束之后就该释放了。所以LeakCanary 默认只检测带Activity的对象,因为只有Activity对象的生命周期能自动监测到。而其他的对象我们可以在确定某个对象会被释放时手动调用RefWatcher的watch来手动监测。
高级功能:
- 设置保存的Leak Trace
默认是保存7个,可以自定义数量。
RefWatcher refWatcher = LeakCanary.refWatcher(this) .maxStoredHeapDumps(42) .buildAndInstall();
- 上传到服务器
public class LeakUploadService extends DisplayLeakService { @Override protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) { if (!result.leakFound || result.excludedLeak) { return; } myServer.uploadLeakBlocking(heapDump.heapDumpFile, leakInfo); } } public class DebugExampleApplication extends ExampleApplication { @Override protected RefWatcher installLeakCanary() { RefWatcher refWatcher = LeakCanary.refWatcher(this) .listenerServiceClass(LeakUploadService.class); .buildAndInstall(); return refWatcher; } }
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <application android:name="com.example.DebugExampleApplication"> <service android:name="com.example.LeakUploadService" /> </application> </manifest>
- 忽略特别对象
public class DebugExampleApplication extends ExampleApplication { @Override protected RefWatcher installLeakCanary() { ExcludedRefs excludedRefs = AndroidExcludedRefs.createAppDefaults() .instanceField("com.example.ExampleClass", "exampleField") .build(); RefWatcher refWatcher = LeakCanary.refWatcher(this) .excludedRefs(excludedRefs) .buildAndInstall(); return refWatcher; } }
- 忽略某个Activity
public class DebugExampleApplication extends ExampleApplication { @Override protected RefWatcher installLeakCanary() { LeakCanary.enableDisplayLeakActivity(this); RefWatcher refWatcher = LeakCanary.refWatcher(this) // Notice we call build() instead of buildAndInstall() .build(); registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { public void onActivityDestroyed(Activity activity) { if (activity instanceof ThirdPartyActivity) { return; } refWatcher.watch(activity); } // ... }); return refWatcher; } }
Android常规的内存泄漏
- Context泄漏
Android的Context使用非常普遍,跟系统打交道的地方大多数情况需要Context。我们需要注意在Activity里调用需要Context的时候尽量不要直接传入Activity进去,避免调用的方法持有你的Activity导致Activity泄漏。再我们编写的方法需要Context的时候,也要尽量不要持有穿过类的Context对象,而是获取ApplicaionContext,从而避免了传过来的是一个Activity。
- Static Activities,Static Views,Static big Objeces…
由于静态变量不会被任何对象持有,也就无法自动被释放。需要手动释放,所有尽量避免出现静态的Activity View ….
- 内部类和匿名类
内部类和匿名类都持有外部类的对象,所以再使用匿名类和内部类的时候避免持有外部实例,如果内部类和匿名类本身的生命周期无法和外部类保持一直(外部类销毁时内部类可能不会或者不会被销毁)时最好才用静态类部类
- 注册某些系统Listener
千万别忘了取消注册