Android工具:LeakCanary—内存泄露检测神器

一、LeakCanary简介
LeakCanary是Square公司开源的一个检测内存的泄露的函数库,可以方便地和你的项目进行集成,在Debug版本中监控Activity、Fragment等的内存泄露;
LeakCanary集成到项目中之后,在检测到内存泄露时,会发送消息到系统通知栏。点击后打开名称DisplayLeakActivity的页面,并显示泄露的跟踪信息,Logcat上面也会有对应的日志输出。同时如果跟踪信息不足以定位时,DisplayLeakActivity还为开发者默认保存了最近7个dump文件到App的目录中,可以使用MAT等工具对dump文件进行进一步的分析;
二、内存泄漏简介
在了解了LeakCanary的接入方式后,我们肯定着急想见识见识LeakCanary的威力。在跟大家演示LeakCanary检测和处理构成之前,大家应该明应该对内存泄露有基本的了解和认识;
1.为什么会产生内存泄漏?
当一个对象不需要使用本该回收时,有另外一个正在使用的对象持有它的引用,从而导致它不能回收停留在堆内存中,这就产生了内存泄漏;
2.内存泄露对程序产生的影响?
内存泄漏是造成应用程序OOM的主要原因之一。Android系统为每个应用程序分配有限的内存,当应用中内存泄漏较多时,就难免会导致应用所需要的内存超出系统分配限额,从而导致OOM应用Crash;
3.Android常见的内存泄露?
相信内存泄露对大家都早有耳闻,但是它不像一些Java异常情况,会立即造成程序的Crash,却有让大家比较“陌生”。下面我们就列举出日常开发中常见的内存泄露类型,让大家对内存泄露的认识不仅仅停留在“有所耳闻 ”的层面;
 单例造成:由于单例静态特性使得单例的生命周期和应用的生命周期一样长,如果一个对象(如Context)已经不使用了,而单例对象还持有对象的引用造成这个对象不能正常被回收;
 非静态内部类创建静态实例造成:在Acitivity内存创建一个非静态内部类单例,避免每次启动资源重新创建。但是因为非静态内部类默认持有外部类(Activity)的引用,并且使用该类创建静态实例。造成该实例和应用生命周期一样长,导致静态实例持有引用的Activity和资源不能正常回收;
 Handler造成:子线程执行网络任务,使用Handler处理子线程发送消息。由于handler对象是非静态匿名内部类的对象,持有外部类(Activity)的引用。在Handler-Message中Looper线程不断轮询处理消息,当Activity退出还有未处理或者正在处理的消息时,消息队列中的消息持有handler对象引用,handler又持有Activity,导致Activity的内存和资源不能及时回收;
 线程造成:匿名内部类Runnalbe和AsyncTask对象执行异步任务,对当前Activity隐式引用。当Activity销毁之前,任务还没有执行完,将导致Activity的内存和资源不能及时回收;
 资源未关闭造成的内存泄露:对于使用了BroadcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄露;
三、LeakCanary接入
下面我们还是以QProject项目进行演示如何在项目中接入LeakCanary,项目目录如下:

1.添加LeakCanary依赖
在主项目main模块的build.gradle文件中添加LeakCanary相关依赖;
/main/build.gradle文件
  1. apply plugin: ‘com.android.application’  
  2. android {  
  3.     …  …  
  4. }  
  5. dependencies {  
  6.     … …  
  7.     //添加leakcanary相关的依赖  
  8.     //在release和test版本中,使用的是LeakCanary的no-op版本,也就是没有实际代码和操作的Wrapper版本,只包含LeakCanary和RefWatcher类的空实现,这样不会对生成的APK包体积和应用性能造成影响  
  9.     debugCompile ‘com.squareup.leakcanary:leakcanary-android:1.5’  
  10.     releaseCompile ‘com.squareup.leakcanary:leakcanary-android-no-op:1.5’  
  11.     testCompile ‘com.squareup.leakcanary:leakcanary-android-no-op:1.5’  
  12.     … …  
  13.     compile project(‘:test’)  
  14. }  
apply plugin: 'com.android.application'
android {
    ...  ...
}
dependencies {
    ... ...
    //添加leakcanary相关的依赖
    //在release和test版本中,使用的是LeakCanary的no-op版本,也就是没有实际代码和操作的Wrapper版本,只包含LeakCanary和RefWatcher类的空实现,这样不会对生成的APK包体积和应用性能造成影响
    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'
    ... ...
    compile project(':test')
}
2.初始化LeakCanary
在主项目main模块的QApplication的onCreate()方法中初始化LeakCanary;
/main/src/main/java/com/qproject/main/QApplication.java文件
  1. public class QAplication extends Application{  
  2.     @Override  
  3.     public void onCreate() {  
  4.         super.onCreate();  
  5.         … …  
  6.         //初始化LeakCanary  
  7.         if (LeakCanary.isInAnalyzerProcess(this)) {  
  8.             return;  
  9.         }  
  10.         LeakCanary.install(this);  
  11.     }  
  12. }  
public class QAplication extends Application{
    @Override
    public void onCreate() {
        super.onCreate();
        ... ...
        //初始化LeakCanary
        if (LeakCanary.isInAnalyzerProcess(this)) {
            return;
        }
        LeakCanary.install(this);
    }
}
OK,到这里我们就完成了一个项目的LeankCanary的简单接入;
提示1:集成LeakCanary后,构建和安装apk的时候报错如下:
Error:Error converting bytecode to dex:
Cause: com.android.dex.DexException: Multiple dex files define Lcom/squareup/leakcanary/LeakCanary;
Error:Execution failed for task ‘:main:transformClassesWithDexForDebug’.
> com.android.build.api.transform.TransformException: com.android.ide.common.process.ProcessException: java.util.concurrent.ExecutionException: java.lang.UnsupportedOperationException
处理1:添加依赖debugCompile ‘com.squareup.haha:haha:2.0.3’,修改依赖的版本为1.4-beta2;
四、LeakCanary检测
通过对一些常见的内存泄露的学习,我们已经对内存泄露有所见闻了。那么下面我们通过LeakCanary工具,让大家感受下它在你的日常开发中的真实存在。我们以常见内存—单例造成的内存泄露为例进行实践;
1.单例内存泄露模拟
test/src/main/com/qproject/test/TestManager.java
  1. public class TestManager {  
  2.     //单例静态特性使得单例的生命周期和应用的生命周期一样长  
  3.     private static TestManager instance;  
  4.     private Context context;  
  5.   
  6.     /** 
  7.      * 传入的Context的生命周期很重要: 
  8.      *   如果传入的是Application的Context,则生命周期和单例生命周期一样长; 
  9.      *   如果传入的是Activity的Context,由于该Context和Activity的生命周期一样长,当Activity退出的时候它的内存不会被回收,因为单例对象持有它的引用; 
  10.      */  
  11.     private TestManager(Context context) {  
  12.         this.context = context;  
  13.     }  
  14.   
  15.     public static TestManager getInstance(Context context) {  
  16.         if (instance == null) {  
  17.             instance = new TestManager(context);  
  18.         }  
  19.         return instance;  
  20.     }  
  21. }  
public class TestManager {
    //单例静态特性使得单例的生命周期和应用的生命周期一样长
    private static TestManager instance;
    private Context context;

    /**
     * 传入的Context的生命周期很重要:
     *   如果传入的是Application的Context,则生命周期和单例生命周期一样长;
     *   如果传入的是Activity的Context,由于该Context和Activity的生命周期一样长,当Activity退出的时候它的内存不会被回收,因为单例对象持有它的引用;
     */
    private TestManager(Context context) {
        this.context = context;
    }

    public static TestManager getInstance(Context context) {
        if (instance == null) {
            instance = new TestManager(context);
        }
        return instance;
    }
}
test/src/main/com/qproject/test/leakcanary/LeakCanaryActivity.java
  1. public class LeakCanaryActivity extends AppCompatActivity {  
  2.   
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_leakcanary);  
  7.         //获取单例对象,退出Activity即可模拟出内存泄露  
  8.         TestManager testManager = TestManager.getInstance(this);  
  9.     }  
  10. }  
public class LeakCanaryActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leakcanary);
        //获取单例对象,退出Activity即可模拟出内存泄露
        TestManager testManager = TestManager.getInstance(this);
    }
}
2.检测消息通知
运行App到LeakCanaryActivit页面并退出,在检测到内存泄露的时候,会发送消息到系统通知栏;
 
3.查看检测详情
点击通知消息,打开名为DisplayLeakActivity的页面,并显示泄漏的跟踪信息;

4.查看LogCat日志
除了以上泄漏信息的显示,Logcat上面也会有对应的日志输出;
  1. //内存泄露对象com.qproject.test.leakcanary.LeakCanaryActivity  
  2. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * com.qproject.test.leakcanary.LeakCanaryActivity has leaked:  
  3. //static com.qproject.test.TestManager.instance的com.qproject.test.TestManager.context引用了回收的内存对象  
  4. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * GC ROOT static com.qproject.test.TestManager.instance  
  5. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * references com.qproject.test.TestManager.context  
  6. //内存泄露对象大小,Reference Key,Device和Android Version等信息  
  7. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * leaks com.qproject.test.leakcanary.LeakCanaryActivity instance  
  8. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Retaining: 46 KB.  
  9. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Reference Key: 3d74d294-70dc-4447-a9a2-64e656ea86b8  
  10. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Device: Genymotion Android PREVIEW - Google Nexus 5X - 7.0.0 - API 24 - 1080x1920 vbox86p  
  11. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Android Version: 7.0 API: 24 LeakCanary: 1.5 00f37f5  
  12. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Durations: watch=5038ms, gc=137ms, heap dump=2390ms, analysis=27325ms  
  13. //内存泄露对象详细信息  
  14. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Details:  
  15. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Class com.qproject.test.TestManager  
  16. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static instance = com.qproject.test.TestManager@316184816 (0x12d898f0)  
  17. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static classOverhead&nbsp;=&nbsp;byte[308]@316175745&nbsp;(0x12d87581)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;*&nbsp;Instance&nbsp;of&nbsp;com.qproject.test.TestManager&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;static&nbsp;instance&nbsp;=&nbsp;com.qproject.test.TestManager@316184816&nbsp;(0x12d898f0)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;static&nbsp;classOverhead = byte[308]@316175745 (0x12d87581)  
  18. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   context = com.qproject.test.leakcanary.LeakCanaryActivity@315059712 (0x12c76e00)  
  19. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   shadow_klass_&nbsp;=&nbsp;com.qproject.test.TestManager&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;shadow_monitor_ = 0  
  20. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Instance of com.qproject.test.leakcanary.LeakCanaryActivity  
  21. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static classOverhead&nbsp;=&nbsp;byte[2228]@316203009&nbsp;(0x12d8e001)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mDelegate&nbsp;=&nbsp;android.support.v7.app.AppCompatDelegateImplN@315842128&nbsp;(0x12d35e50)&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mEatKeyUpEvent&nbsp;=&nbsp;false&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mResources&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mThemeId&nbsp;=&nbsp;2131230884&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mCreated&nbsp;=&nbsp;true&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mFragments&nbsp;=&nbsp;android.support.v4.app.FragmentController@316183584&nbsp;(0x12d89420)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mHandler&nbsp;=&nbsp;android.support.v4.app.FragmentActivity1@316163360 (0x12d84520)  
  22. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mNextCandidateRequestIndex = 0  
  23. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mOptionsMenuInvalidated = false  
  24. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mPendingFragmentActivityResults = android.support.v4.util.SparseArrayCompat@316172368 (0x12d86850)  
  25. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mReallyStopped = true  
  26. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mRequestedPermissionsFromFragment = false  
  27. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mResumed = false  
  28. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mRetaining = false  
  29. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mStopped = true  
  30. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mStartedActivityFromFragment = false  
  31. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mStartedIntentSenderFromFragment = false  
  32. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mExtraDataMap = android.support.v4.util.SimpleArrayMap@316171864 (0x12d86658)  
  33. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mActionBar = null  
  34. 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mActionModeTypeStarting = 0  
  35. 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mActivityInfo = android.content.pm.ActivityInfo@315841984 (0x12d35dc0)  
  36. 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mActivityTransitionState = android.app.ActivityTransitionState@316207336 (0x12d8f0e8)  
  37. 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mApplication = com.qproject.main.QAplication@314916416 (0x12c53e40)  
  38. 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mCalled = true  
  39. 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mChangeCanvasToTranslucent = false  
  40. 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mChangingConfigurations = false  
  41. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mComponent = android.content.ComponentName@315998320 (0x12d5c070)  
  42. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mConfigChangeFlags = 0  
  43. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mCurrentConfig = android.content.res.Configuration@316178888 (0x12d881c8)  
  44. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDecor = null  
  45. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDefaultKeyMode = 0  
  46. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDefaultKeySsb = null  
  47. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDestroyed = true  
  48. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDoReportFullyDrawn = false  
  49. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEatKeyUpEvent = false  
  50. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEmbeddedID = null  
  51. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEnableDefaultActionBarUp = false  
  52. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEnterTransitionListener = android.app.SharedElementCallback1@1887062680&nbsp;(0x707a4a98)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.713&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mExitTransitionListener&nbsp;=&nbsp;android.app.SharedElementCallback1@1887062680 (0x707a4a98)  
  53. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mFinished = true  
  54. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mFragments = android.app.FragmentController@316183536 (0x12d893f0)  
  55. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mHandler = android.os.Handler@316163296 (0x12d844e0)  
  56. 12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mHasCurrentPermissionsRequest = false  
  57. 12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mIdent = 20356640  
  58. 12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mInstanceTracker = android.os.StrictModeInstanceTracker@316183552&nbsp;(0x12d89400)&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.714&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mInstrumentation&nbsp;=&nbsp;android.app.Instrumentation@314950632&nbsp;(0x12c5c3e8)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.714&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mIntent&nbsp;=&nbsp;android.content.Intent@316215416&nbsp;(0x12d91078)&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.714&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mLastNonConfigurationInstances&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.714&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mMainThread&nbsp;=&nbsp;android.app.ActivityThread@314966272&nbsp;(0x12c60100)&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mManagedCursors&nbsp;=&nbsp;java.util.ArrayList@316171816&nbsp;(0x12d86628)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mManagedDialogs&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mMenuInflater&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mParent&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mReferrer&nbsp;=&nbsp;java.lang.String@316215864&nbsp;(0x12d91238)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mResultCode&nbsp;=&nbsp;0&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mResultData&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mResumed&nbsp;=&nbsp;false&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mSearchEvent&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mSearchManager&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mStartedActivity&nbsp;=&nbsp;false&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mStopped&nbsp;=&nbsp;true&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mTaskDescription&nbsp;=&nbsp;android.app.ActivityManagerTaskDescription@316163328 (0x12d84500)  
  59. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTemporaryPause = false  
  60. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTitle = java.lang.String@315129824 (0x12c87fe0)  
  61. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTitleColor = 0  
  62. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTitleReady = true  
  63. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mToken = android.os.BinderProxy@315867328 (0x12d3c0c0)  
  64. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTranslucentCallback = null  
  65. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mUiThread = java.lang.Thread@1959751680 (0x74cf7000)  
  66. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVisibleBehind = false  
  67. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVisibleFromClient = true  
  68. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVisibleFromServer = true  
  69. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVoiceInteractor = null  
  70. 12-25 07:50:51.716 4941-5795/com.qproject.main D/LeakCanary: |   mWindow = com.android.internal.policy.PhoneWindow@315116864 (0x12c84d40)  
  71. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mWindowAdded = true  
  72. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mWindowManager = android.view.WindowManagerImpl@316172152 (0x12d86778)  
  73. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mInflater = com.android.internal.policy.PhoneLayoutInflater@316010352 (0x12d5ef70)  
  74. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mOverrideConfiguration = null  
  75. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mResources = android.content.res.Resources@316235992 (0x12d960d8)  
  76. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mTheme = android.content.res.ResourcesTheme@316183728&nbsp;(0x12d894b0)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.717&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mThemeResource&nbsp;=&nbsp;2131230884&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.717&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mBase&nbsp;=&nbsp;android.app.ContextImpl@316155392&nbsp;(0x12d82600)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.717&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;shadow_klass_ = com.qproject.test.leakcanary.LeakCanaryActivity  
  77. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   shadow_monitor_&nbsp;=&nbsp;1316364430&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.717&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;*&nbsp;Excluded&nbsp;Refs:&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.717&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;Field:&nbsp;android.view.ChoreographerFrameDisplayEventReceiver.mMessageQueue (always)  
  78. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Thread:FinalizerWatchdogDaemon (always)  
  79. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Thread:main (always)  
  80. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Thread:LeakCanary-Heap-Dump (always)  
  81. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.WeakReference (always)  
  82. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.SoftReference (always)  
  83. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.PhantomReference (always)  
  84. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.Finalizer (always)  
  85. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.FinalizerReference (always)  
//内存泄露对象com.qproject.test.leakcanary.LeakCanaryActivity
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * com.qproject.test.leakcanary.LeakCanaryActivity has leaked:
//static com.qproject.test.TestManager.instance的com.qproject.test.TestManager.context引用了回收的内存对象
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * GC ROOT static com.qproject.test.TestManager.instance
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * references com.qproject.test.TestManager.context
//内存泄露对象大小,Reference Key,Device和Android Version等信息
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * leaks com.qproject.test.leakcanary.LeakCanaryActivity instance
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Retaining: 46 KB.
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Reference Key: 3d74d294-70dc-4447-a9a2-64e656ea86b8
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Device: Genymotion Android PREVIEW - Google Nexus 5X - 7.0.0 - API 24 - 1080x1920 vbox86p
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Android Version: 7.0 API: 24 LeakCanary: 1.5 00f37f5
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Durations: watch=5038ms, gc=137ms, heap dump=2390ms, analysis=27325ms
//内存泄露对象详细信息
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Details:
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Class com.qproject.test.TestManager
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static instance = com.qproject.test.TestManager@316184816 (0x12d898f0)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static $classOverhead = byte[308]@316175745 (0x12d87581)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Instance of com.qproject.test.TestManager
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static instance = com.qproject.test.TestManager@316184816 (0x12d898f0)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static $classOverhead = byte[308]@316175745 (0x12d87581)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   context = com.qproject.test.leakcanary.LeakCanaryActivity@315059712 (0x12c76e00)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   shadow$_klass_ = com.qproject.test.TestManager
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   shadow$_monitor_ = 0
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Instance of com.qproject.test.leakcanary.LeakCanaryActivity
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static $classOverhead = byte[2228]@316203009 (0x12d8e001)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mDelegate = android.support.v7.app.AppCompatDelegateImplN@315842128 (0x12d35e50)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mEatKeyUpEvent = false
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mResources = null
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mThemeId = 2131230884
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mCreated = true
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mFragments = android.support.v4.app.FragmentController@316183584 (0x12d89420)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mHandler = android.support.v4.app.FragmentActivity$1@316163360 (0x12d84520)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mNextCandidateRequestIndex = 0
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mOptionsMenuInvalidated = false
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mPendingFragmentActivityResults = android.support.v4.util.SparseArrayCompat@316172368 (0x12d86850)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mReallyStopped = true
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mRequestedPermissionsFromFragment = false
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mResumed = false
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mRetaining = false
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mStopped = true
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mStartedActivityFromFragment = false
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mStartedIntentSenderFromFragment = false
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mExtraDataMap = android.support.v4.util.SimpleArrayMap@316171864 (0x12d86658)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mActionBar = null
12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mActionModeTypeStarting = 0
12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mActivityInfo = android.content.pm.ActivityInfo@315841984 (0x12d35dc0)
12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mActivityTransitionState = android.app.ActivityTransitionState@316207336 (0x12d8f0e8)
12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mApplication = com.qproject.main.QAplication@314916416 (0x12c53e40)
12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mCalled = true
12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mChangeCanvasToTranslucent = false
12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mChangingConfigurations = false
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mComponent = android.content.ComponentName@315998320 (0x12d5c070)
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mConfigChangeFlags = 0
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mCurrentConfig = android.content.res.Configuration@316178888 (0x12d881c8)
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDecor = null
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDefaultKeyMode = 0
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDefaultKeySsb = null
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDestroyed = true
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDoReportFullyDrawn = false
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEatKeyUpEvent = false
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEmbeddedID = null
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEnableDefaultActionBarUp = false
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEnterTransitionListener = android.app.SharedElementCallback$1@1887062680 (0x707a4a98)
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mExitTransitionListener = android.app.SharedElementCallback$1@1887062680 (0x707a4a98)
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mFinished = true
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mFragments = android.app.FragmentController@316183536 (0x12d893f0)
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mHandler = android.os.Handler@316163296 (0x12d844e0)
12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mHasCurrentPermissionsRequest = false
12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mIdent = 20356640
12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mInstanceTracker = android.os.StrictMode$InstanceTracker@316183552 (0x12d89400)
12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mInstrumentation = android.app.Instrumentation@314950632 (0x12c5c3e8)
12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mIntent = android.content.Intent@316215416 (0x12d91078)
12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mLastNonConfigurationInstances = null
12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mMainThread = android.app.ActivityThread@314966272 (0x12c60100)
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mManagedCursors = java.util.ArrayList@316171816 (0x12d86628)
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mManagedDialogs = null
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mMenuInflater = null
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mParent = null
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mReferrer = java.lang.String@316215864 (0x12d91238)
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mResultCode = 0
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mResultData = null
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mResumed = false
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mSearchEvent = null
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mSearchManager = null
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mStartedActivity = false
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mStopped = true
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTaskDescription = android.app.ActivityManager$TaskDescription@316163328 (0x12d84500)
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTemporaryPause = false
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTitle = java.lang.String@315129824 (0x12c87fe0)
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTitleColor = 0
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTitleReady = true
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mToken = android.os.BinderProxy@315867328 (0x12d3c0c0)
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTranslucentCallback = null
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mUiThread = java.lang.Thread@1959751680 (0x74cf7000)
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVisibleBehind = false
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVisibleFromClient = true
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVisibleFromServer = true
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVoiceInteractor = null
12-25 07:50:51.716 4941-5795/com.qproject.main D/LeakCanary: |   mWindow = com.android.internal.policy.PhoneWindow@315116864 (0x12c84d40)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mWindowAdded = true
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mWindowManager = android.view.WindowManagerImpl@316172152 (0x12d86778)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mInflater = com.android.internal.policy.PhoneLayoutInflater@316010352 (0x12d5ef70)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mOverrideConfiguration = null
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mResources = android.content.res.Resources@316235992 (0x12d960d8)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mTheme = android.content.res.Resources$Theme@316183728 (0x12d894b0)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mThemeResource = 2131230884
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mBase = android.app.ContextImpl@316155392 (0x12d82600)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   shadow$_klass_ = com.qproject.test.leakcanary.LeakCanaryActivity
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   shadow$_monitor_ = 1316364430
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: * Excluded Refs:
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Field: android.view.Choreographer$FrameDisplayEventReceiver.mMessageQueue (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Thread:FinalizerWatchdogDaemon (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Thread:main (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Thread:LeakCanary-Heap-Dump (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.WeakReference (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.SoftReference (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.PhantomReference (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.Finalizer (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.FinalizerReference (always)
5.获取dump日志文件
如果觉得跟踪信息不足以定位时,DisplayLeakActivity还为开发者默认保存了最近7个dump文件到APP的目录中,可以使用MAT等工具对dump文件进行进一步的分析;
  1. vbox86p:/data/data/com.qproject.main/files/leakcanary # ls  
  2. 2016-12-25_07-50-51_718.hprof 2016-12-25_07-50-51_718.hprof.result  
  3. D:\>adb pull ./data/data/com.qproject.main/files/leakcanary/2016-12-25_07-50-51_ 718.hprof  
  4. [100%] ./data/data/com.qproject.main/f…akcanary/2016-12-25_07-50-51_718.hprof  
vbox86p:/data/data/com.qproject.main/files/leakcanary # ls
2016-12-25_07-50-51_718.hprof 2016-12-25_07-50-51_718.hprof.result
D:\>adb pull ./data/data/com.qproject.main/files/leakcanary/2016-12-25_07-50-51_ 718.hprof
[100%] ./data/data/com.qproject.main/f...akcanary/2016-12-25_07-50-51_718.hprof
Android Studio->View->Tool Windows->Captures,打开Captures窗口,将pull获取的hprof文件剪切到Capture中文件的目录下,双击打开即可;

具体的分析过程,这里就不重点讲述,大家去查询MAT相关的资料;
五、检测其他对象
如果想监听其他的对象(例如Fragment ),可以通过RefWatcher的实例来实现;
 
六、LeakCanary原理
1.RefWatcher.watch()函数会为被监控的对象创建一个KeyedWeakReference弱引用对象,是WeakReference对的子类,增加了键值对信息,后面会根据指定的键key找到弱引用对象;
2.在后台线程AndroidWatchExecutor中,检查KeyedWeakReference弱引用是否已经被清楚。如果还存在,则触发一次垃圾回收之后。垃圾回收之后,如果弱引用对象依然存在,说明发生了内存泄露;
一、LeakCanary简介
LeakCanary是Square公司开源的一个检测内存的泄露的函数库,可以方便地和你的项目进行集成,在Debug版本中监控Activity、Fragment等的内存泄露;
LeakCanary集成到项目中之后,在检测到内存泄露时,会发送消息到系统通知栏。点击后打开名称DisplayLeakActivity的页面,并显示泄露的跟踪信息,Logcat上面也会有对应的日志输出。同时如果跟踪信息不足以定位时,DisplayLeakActivity还为开发者默认保存了最近7个dump文件到App的目录中,可以使用MAT等工具对dump文件进行进一步的分析;
二、内存泄漏简介
在了解了LeakCanary的接入方式后,我们肯定着急想见识见识LeakCanary的威力。在跟大家演示LeakCanary检测和处理构成之前,大家应该明应该对内存泄露有基本的了解和认识;
1.为什么会产生内存泄漏?
当一个对象不需要使用本该回收时,有另外一个正在使用的对象持有它的引用,从而导致它不能回收停留在堆内存中,这就产生了内存泄漏;
2.内存泄露对程序产生的影响?
内存泄漏是造成应用程序OOM的主要原因之一。Android系统为每个应用程序分配有限的内存,当应用中内存泄漏较多时,就难免会导致应用所需要的内存超出系统分配限额,从而导致OOM应用Crash;
3.Android常见的内存泄露?
相信内存泄露对大家都早有耳闻,但是它不像一些Java异常情况,会立即造成程序的Crash,却有让大家比较“陌生”。下面我们就列举出日常开发中常见的内存泄露类型,让大家对内存泄露的认识不仅仅停留在“有所耳闻 ”的层面;
 单例造成:由于单例静态特性使得单例的生命周期和应用的生命周期一样长,如果一个对象(如Context)已经不使用了,而单例对象还持有对象的引用造成这个对象不能正常被回收;
 非静态内部类创建静态实例造成:在Acitivity内存创建一个非静态内部类单例,避免每次启动资源重新创建。但是因为非静态内部类默认持有外部类(Activity)的引用,并且使用该类创建静态实例。造成该实例和应用生命周期一样长,导致静态实例持有引用的Activity和资源不能正常回收;
 Handler造成:子线程执行网络任务,使用Handler处理子线程发送消息。由于handler对象是非静态匿名内部类的对象,持有外部类(Activity)的引用。在Handler-Message中Looper线程不断轮询处理消息,当Activity退出还有未处理或者正在处理的消息时,消息队列中的消息持有handler对象引用,handler又持有Activity,导致Activity的内存和资源不能及时回收;
 线程造成:匿名内部类Runnalbe和AsyncTask对象执行异步任务,对当前Activity隐式引用。当Activity销毁之前,任务还没有执行完,将导致Activity的内存和资源不能及时回收;
 资源未关闭造成的内存泄露:对于使用了BroadcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄露;
三、LeakCanary接入
下面我们还是以QProject项目进行演示如何在项目中接入LeakCanary,项目目录如下:

1.添加LeakCanary依赖
在主项目main模块的build.gradle文件中添加LeakCanary相关依赖;
/main/build.gradle文件
  1. apply plugin: ‘com.android.application’  
  2. android {  
  3.     …  …  
  4. }  
  5. dependencies {  
  6.     … …  
  7.     //添加leakcanary相关的依赖  
  8.     //在release和test版本中,使用的是LeakCanary的no-op版本,也就是没有实际代码和操作的Wrapper版本,只包含LeakCanary和RefWatcher类的空实现,这样不会对生成的APK包体积和应用性能造成影响  
  9.     debugCompile ‘com.squareup.leakcanary:leakcanary-android:1.5’  
  10.     releaseCompile ‘com.squareup.leakcanary:leakcanary-android-no-op:1.5’  
  11.     testCompile ‘com.squareup.leakcanary:leakcanary-android-no-op:1.5’  
  12.     … …  
  13.     compile project(‘:test’)  
  14. }  
apply plugin: 'com.android.application'
android {
    ...  ...
}
dependencies {
    ... ...
    //添加leakcanary相关的依赖
    //在release和test版本中,使用的是LeakCanary的no-op版本,也就是没有实际代码和操作的Wrapper版本,只包含LeakCanary和RefWatcher类的空实现,这样不会对生成的APK包体积和应用性能造成影响
    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'
    ... ...
    compile project(':test')
}
2.初始化LeakCanary
在主项目main模块的QApplication的onCreate()方法中初始化LeakCanary;
/main/src/main/java/com/qproject/main/QApplication.java文件
  1. public class QAplication extends Application{  
  2.     @Override  
  3.     public void onCreate() {  
  4.         super.onCreate();  
  5.         … …  
  6.         //初始化LeakCanary  
  7.         if (LeakCanary.isInAnalyzerProcess(this)) {  
  8.             return;  
  9.         }  
  10.         LeakCanary.install(this);  
  11.     }  
  12. }  
public class QAplication extends Application{
    @Override
    public void onCreate() {
        super.onCreate();
        ... ...
        //初始化LeakCanary
        if (LeakCanary.isInAnalyzerProcess(this)) {
            return;
        }
        LeakCanary.install(this);
    }
}
OK,到这里我们就完成了一个项目的LeankCanary的简单接入;
提示1:集成LeakCanary后,构建和安装apk的时候报错如下:
Error:Error converting bytecode to dex:
Cause: com.android.dex.DexException: Multiple dex files define Lcom/squareup/leakcanary/LeakCanary;
Error:Execution failed for task ‘:main:transformClassesWithDexForDebug’.
> com.android.build.api.transform.TransformException: com.android.ide.common.process.ProcessException: java.util.concurrent.ExecutionException: java.lang.UnsupportedOperationException
处理1:添加依赖debugCompile ‘com.squareup.haha:haha:2.0.3’,修改依赖的版本为1.4-beta2;
四、LeakCanary检测
通过对一些常见的内存泄露的学习,我们已经对内存泄露有所见闻了。那么下面我们通过LeakCanary工具,让大家感受下它在你的日常开发中的真实存在。我们以常见内存—单例造成的内存泄露为例进行实践;
1.单例内存泄露模拟
test/src/main/com/qproject/test/TestManager.java
  1. public class TestManager {  
  2.     //单例静态特性使得单例的生命周期和应用的生命周期一样长  
  3.     private static TestManager instance;  
  4.     private Context context;  
  5.   
  6.     /** 
  7.      * 传入的Context的生命周期很重要: 
  8.      *   如果传入的是Application的Context,则生命周期和单例生命周期一样长; 
  9.      *   如果传入的是Activity的Context,由于该Context和Activity的生命周期一样长,当Activity退出的时候它的内存不会被回收,因为单例对象持有它的引用; 
  10.      */  
  11.     private TestManager(Context context) {  
  12.         this.context = context;  
  13.     }  
  14.   
  15.     public static TestManager getInstance(Context context) {  
  16.         if (instance == null) {  
  17.             instance = new TestManager(context);  
  18.         }  
  19.         return instance;  
  20.     }  
  21. }  
public class TestManager {
    //单例静态特性使得单例的生命周期和应用的生命周期一样长
    private static TestManager instance;
    private Context context;

    /**
     * 传入的Context的生命周期很重要:
     *   如果传入的是Application的Context,则生命周期和单例生命周期一样长;
     *   如果传入的是Activity的Context,由于该Context和Activity的生命周期一样长,当Activity退出的时候它的内存不会被回收,因为单例对象持有它的引用;
     */
    private TestManager(Context context) {
        this.context = context;
    }

    public static TestManager getInstance(Context context) {
        if (instance == null) {
            instance = new TestManager(context);
        }
        return instance;
    }
}
test/src/main/com/qproject/test/leakcanary/LeakCanaryActivity.java
  1. public class LeakCanaryActivity extends AppCompatActivity {  
  2.   
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_leakcanary);  
  7.         //获取单例对象,退出Activity即可模拟出内存泄露  
  8.         TestManager testManager = TestManager.getInstance(this);  
  9.     }  
  10. }  
public class LeakCanaryActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_leakcanary);
        //获取单例对象,退出Activity即可模拟出内存泄露
        TestManager testManager = TestManager.getInstance(this);
    }
}
2.检测消息通知
运行App到LeakCanaryActivit页面并退出,在检测到内存泄露的时候,会发送消息到系统通知栏;
 
3.查看检测详情
点击通知消息,打开名为DisplayLeakActivity的页面,并显示泄漏的跟踪信息;

4.查看LogCat日志
除了以上泄漏信息的显示,Logcat上面也会有对应的日志输出;
  1. //内存泄露对象com.qproject.test.leakcanary.LeakCanaryActivity  
  2. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * com.qproject.test.leakcanary.LeakCanaryActivity has leaked:  
  3. //static com.qproject.test.TestManager.instance的com.qproject.test.TestManager.context引用了回收的内存对象  
  4. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * GC ROOT static com.qproject.test.TestManager.instance  
  5. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * references com.qproject.test.TestManager.context  
  6. //内存泄露对象大小,Reference Key,Device和Android Version等信息  
  7. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * leaks com.qproject.test.leakcanary.LeakCanaryActivity instance  
  8. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Retaining: 46 KB.  
  9. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Reference Key: 3d74d294-70dc-4447-a9a2-64e656ea86b8  
  10. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Device: Genymotion Android PREVIEW - Google Nexus 5X - 7.0.0 - API 24 - 1080x1920 vbox86p  
  11. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Android Version: 7.0 API: 24 LeakCanary: 1.5 00f37f5  
  12. 12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Durations: watch=5038ms, gc=137ms, heap dump=2390ms, analysis=27325ms  
  13. //内存泄露对象详细信息  
  14. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Details:  
  15. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Class com.qproject.test.TestManager  
  16. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static instance = com.qproject.test.TestManager@316184816 (0x12d898f0)  
  17. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static classOverhead&nbsp;=&nbsp;byte[308]@316175745&nbsp;(0x12d87581)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;*&nbsp;Instance&nbsp;of&nbsp;com.qproject.test.TestManager&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;static&nbsp;instance&nbsp;=&nbsp;com.qproject.test.TestManager@316184816&nbsp;(0x12d898f0)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;static&nbsp;classOverhead = byte[308]@316175745 (0x12d87581)  
  18. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   context = com.qproject.test.leakcanary.LeakCanaryActivity@315059712 (0x12c76e00)  
  19. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   shadow_klass_&nbsp;=&nbsp;com.qproject.test.TestManager&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;shadow_monitor_ = 0  
  20. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Instance of com.qproject.test.leakcanary.LeakCanaryActivity  
  21. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static classOverhead&nbsp;=&nbsp;byte[2228]@316203009&nbsp;(0x12d8e001)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mDelegate&nbsp;=&nbsp;android.support.v7.app.AppCompatDelegateImplN@315842128&nbsp;(0x12d35e50)&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mEatKeyUpEvent&nbsp;=&nbsp;false&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mResources&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mThemeId&nbsp;=&nbsp;2131230884&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mCreated&nbsp;=&nbsp;true&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mFragments&nbsp;=&nbsp;android.support.v4.app.FragmentController@316183584&nbsp;(0x12d89420)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.711&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mHandler&nbsp;=&nbsp;android.support.v4.app.FragmentActivity1@316163360 (0x12d84520)  
  22. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mNextCandidateRequestIndex = 0  
  23. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mOptionsMenuInvalidated = false  
  24. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mPendingFragmentActivityResults = android.support.v4.util.SparseArrayCompat@316172368 (0x12d86850)  
  25. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mReallyStopped = true  
  26. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mRequestedPermissionsFromFragment = false  
  27. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mResumed = false  
  28. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mRetaining = false  
  29. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mStopped = true  
  30. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mStartedActivityFromFragment = false  
  31. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mStartedIntentSenderFromFragment = false  
  32. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mExtraDataMap = android.support.v4.util.SimpleArrayMap@316171864 (0x12d86658)  
  33. 12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mActionBar = null  
  34. 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mActionModeTypeStarting = 0  
  35. 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mActivityInfo = android.content.pm.ActivityInfo@315841984 (0x12d35dc0)  
  36. 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mActivityTransitionState = android.app.ActivityTransitionState@316207336 (0x12d8f0e8)  
  37. 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mApplication = com.qproject.main.QAplication@314916416 (0x12c53e40)  
  38. 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mCalled = true  
  39. 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mChangeCanvasToTranslucent = false  
  40. 12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mChangingConfigurations = false  
  41. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mComponent = android.content.ComponentName@315998320 (0x12d5c070)  
  42. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mConfigChangeFlags = 0  
  43. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mCurrentConfig = android.content.res.Configuration@316178888 (0x12d881c8)  
  44. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDecor = null  
  45. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDefaultKeyMode = 0  
  46. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDefaultKeySsb = null  
  47. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDestroyed = true  
  48. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDoReportFullyDrawn = false  
  49. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEatKeyUpEvent = false  
  50. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEmbeddedID = null  
  51. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEnableDefaultActionBarUp = false  
  52. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEnterTransitionListener = android.app.SharedElementCallback1@1887062680&nbsp;(0x707a4a98)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.713&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mExitTransitionListener&nbsp;=&nbsp;android.app.SharedElementCallback1@1887062680 (0x707a4a98)  
  53. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mFinished = true  
  54. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mFragments = android.app.FragmentController@316183536 (0x12d893f0)  
  55. 12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mHandler = android.os.Handler@316163296 (0x12d844e0)  
  56. 12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mHasCurrentPermissionsRequest = false  
  57. 12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mIdent = 20356640  
  58. 12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mInstanceTracker = android.os.StrictModeInstanceTracker@316183552&nbsp;(0x12d89400)&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.714&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mInstrumentation&nbsp;=&nbsp;android.app.Instrumentation@314950632&nbsp;(0x12c5c3e8)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.714&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mIntent&nbsp;=&nbsp;android.content.Intent@316215416&nbsp;(0x12d91078)&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.714&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mLastNonConfigurationInstances&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.714&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mMainThread&nbsp;=&nbsp;android.app.ActivityThread@314966272&nbsp;(0x12c60100)&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mManagedCursors&nbsp;=&nbsp;java.util.ArrayList@316171816&nbsp;(0x12d86628)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mManagedDialogs&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mMenuInflater&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mParent&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mReferrer&nbsp;=&nbsp;java.lang.String@316215864&nbsp;(0x12d91238)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mResultCode&nbsp;=&nbsp;0&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mResultData&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mResumed&nbsp;=&nbsp;false&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mSearchEvent&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mSearchManager&nbsp;=&nbsp;null&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mStartedActivity&nbsp;=&nbsp;false&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mStopped&nbsp;=&nbsp;true&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.715&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mTaskDescription&nbsp;=&nbsp;android.app.ActivityManagerTaskDescription@316163328 (0x12d84500)  
  59. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTemporaryPause = false  
  60. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTitle = java.lang.String@315129824 (0x12c87fe0)  
  61. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTitleColor = 0  
  62. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTitleReady = true  
  63. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mToken = android.os.BinderProxy@315867328 (0x12d3c0c0)  
  64. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTranslucentCallback = null  
  65. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mUiThread = java.lang.Thread@1959751680 (0x74cf7000)  
  66. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVisibleBehind = false  
  67. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVisibleFromClient = true  
  68. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVisibleFromServer = true  
  69. 12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVoiceInteractor = null  
  70. 12-25 07:50:51.716 4941-5795/com.qproject.main D/LeakCanary: |   mWindow = com.android.internal.policy.PhoneWindow@315116864 (0x12c84d40)  
  71. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mWindowAdded = true  
  72. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mWindowManager = android.view.WindowManagerImpl@316172152 (0x12d86778)  
  73. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mInflater = com.android.internal.policy.PhoneLayoutInflater@316010352 (0x12d5ef70)  
  74. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mOverrideConfiguration = null  
  75. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mResources = android.content.res.Resources@316235992 (0x12d960d8)  
  76. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mTheme = android.content.res.ResourcesTheme@316183728&nbsp;(0x12d894b0)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.717&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mThemeResource&nbsp;=&nbsp;2131230884&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.717&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;mBase&nbsp;=&nbsp;android.app.ContextImpl@316155392&nbsp;(0x12d82600)&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.717&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;&nbsp;&nbsp;shadow_klass_ = com.qproject.test.leakcanary.LeakCanaryActivity  
  77. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   shadow_monitor_&nbsp;=&nbsp;1316364430&nbsp;&nbsp;</span></li><li class=""><span>12-25&nbsp;07:50:51.717&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;*&nbsp;Excluded&nbsp;Refs:&nbsp;&nbsp;</span></li><li class="alt"><span>12-25&nbsp;07:50:51.717&nbsp;4941-5795/com.qproject.main&nbsp;D/LeakCanary:&nbsp;|&nbsp;Field:&nbsp;android.view.ChoreographerFrameDisplayEventReceiver.mMessageQueue (always)  
  78. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Thread:FinalizerWatchdogDaemon (always)  
  79. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Thread:main (always)  
  80. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Thread:LeakCanary-Heap-Dump (always)  
  81. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.WeakReference (always)  
  82. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.SoftReference (always)  
  83. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.PhantomReference (always)  
  84. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.Finalizer (always)  
  85. 12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.FinalizerReference (always)  
//内存泄露对象com.qproject.test.leakcanary.LeakCanaryActivity
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * com.qproject.test.leakcanary.LeakCanaryActivity has leaked:
//static com.qproject.test.TestManager.instance的com.qproject.test.TestManager.context引用了回收的内存对象
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * GC ROOT static com.qproject.test.TestManager.instance
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * references com.qproject.test.TestManager.context
//内存泄露对象大小,Reference Key,Device和Android Version等信息
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * leaks com.qproject.test.leakcanary.LeakCanaryActivity instance
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Retaining: 46 KB.
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Reference Key: 3d74d294-70dc-4447-a9a2-64e656ea86b8
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Device: Genymotion Android PREVIEW - Google Nexus 5X - 7.0.0 - API 24 - 1080x1920 vbox86p
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Android Version: 7.0 API: 24 LeakCanary: 1.5 00f37f5
12-25 07:50:51.710 4941-5795/com.qproject.main D/LeakCanary: * Durations: watch=5038ms, gc=137ms, heap dump=2390ms, analysis=27325ms
//内存泄露对象详细信息
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Details:
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Class com.qproject.test.TestManager
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static instance = com.qproject.test.TestManager@316184816 (0x12d898f0)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static $classOverhead = byte[308]@316175745 (0x12d87581)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Instance of com.qproject.test.TestManager
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static instance = com.qproject.test.TestManager@316184816 (0x12d898f0)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static $classOverhead = byte[308]@316175745 (0x12d87581)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   context = com.qproject.test.leakcanary.LeakCanaryActivity@315059712 (0x12c76e00)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   shadow$_klass_ = com.qproject.test.TestManager
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   shadow$_monitor_ = 0
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: * Instance of com.qproject.test.leakcanary.LeakCanaryActivity
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   static $classOverhead = byte[2228]@316203009 (0x12d8e001)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mDelegate = android.support.v7.app.AppCompatDelegateImplN@315842128 (0x12d35e50)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mEatKeyUpEvent = false
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mResources = null
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mThemeId = 2131230884
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mCreated = true
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mFragments = android.support.v4.app.FragmentController@316183584 (0x12d89420)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mHandler = android.support.v4.app.FragmentActivity$1@316163360 (0x12d84520)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mNextCandidateRequestIndex = 0
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mOptionsMenuInvalidated = false
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mPendingFragmentActivityResults = android.support.v4.util.SparseArrayCompat@316172368 (0x12d86850)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mReallyStopped = true
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mRequestedPermissionsFromFragment = false
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mResumed = false
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mRetaining = false
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mStopped = true
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mStartedActivityFromFragment = false
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mStartedIntentSenderFromFragment = false
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mExtraDataMap = android.support.v4.util.SimpleArrayMap@316171864 (0x12d86658)
12-25 07:50:51.711 4941-5795/com.qproject.main D/LeakCanary: |   mActionBar = null
12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mActionModeTypeStarting = 0
12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mActivityInfo = android.content.pm.ActivityInfo@315841984 (0x12d35dc0)
12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mActivityTransitionState = android.app.ActivityTransitionState@316207336 (0x12d8f0e8)
12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mApplication = com.qproject.main.QAplication@314916416 (0x12c53e40)
12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mCalled = true
12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mChangeCanvasToTranslucent = false
12-25 07:50:51.712 4941-5795/com.qproject.main D/LeakCanary: |   mChangingConfigurations = false
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mComponent = android.content.ComponentName@315998320 (0x12d5c070)
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mConfigChangeFlags = 0
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mCurrentConfig = android.content.res.Configuration@316178888 (0x12d881c8)
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDecor = null
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDefaultKeyMode = 0
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDefaultKeySsb = null
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDestroyed = true
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mDoReportFullyDrawn = false
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEatKeyUpEvent = false
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEmbeddedID = null
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEnableDefaultActionBarUp = false
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mEnterTransitionListener = android.app.SharedElementCallback$1@1887062680 (0x707a4a98)
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mExitTransitionListener = android.app.SharedElementCallback$1@1887062680 (0x707a4a98)
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mFinished = true
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mFragments = android.app.FragmentController@316183536 (0x12d893f0)
12-25 07:50:51.713 4941-5795/com.qproject.main D/LeakCanary: |   mHandler = android.os.Handler@316163296 (0x12d844e0)
12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mHasCurrentPermissionsRequest = false
12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mIdent = 20356640
12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mInstanceTracker = android.os.StrictMode$InstanceTracker@316183552 (0x12d89400)
12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mInstrumentation = android.app.Instrumentation@314950632 (0x12c5c3e8)
12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mIntent = android.content.Intent@316215416 (0x12d91078)
12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mLastNonConfigurationInstances = null
12-25 07:50:51.714 4941-5795/com.qproject.main D/LeakCanary: |   mMainThread = android.app.ActivityThread@314966272 (0x12c60100)
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mManagedCursors = java.util.ArrayList@316171816 (0x12d86628)
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mManagedDialogs = null
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mMenuInflater = null
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mParent = null
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mReferrer = java.lang.String@316215864 (0x12d91238)
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mResultCode = 0
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mResultData = null
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mResumed = false
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mSearchEvent = null
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mSearchManager = null
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mStartedActivity = false
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mStopped = true
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTaskDescription = android.app.ActivityManager$TaskDescription@316163328 (0x12d84500)
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTemporaryPause = false
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTitle = java.lang.String@315129824 (0x12c87fe0)
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTitleColor = 0
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTitleReady = true
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mToken = android.os.BinderProxy@315867328 (0x12d3c0c0)
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mTranslucentCallback = null
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mUiThread = java.lang.Thread@1959751680 (0x74cf7000)
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVisibleBehind = false
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVisibleFromClient = true
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVisibleFromServer = true
12-25 07:50:51.715 4941-5795/com.qproject.main D/LeakCanary: |   mVoiceInteractor = null
12-25 07:50:51.716 4941-5795/com.qproject.main D/LeakCanary: |   mWindow = com.android.internal.policy.PhoneWindow@315116864 (0x12c84d40)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mWindowAdded = true
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mWindowManager = android.view.WindowManagerImpl@316172152 (0x12d86778)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mInflater = com.android.internal.policy.PhoneLayoutInflater@316010352 (0x12d5ef70)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mOverrideConfiguration = null
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mResources = android.content.res.Resources@316235992 (0x12d960d8)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mTheme = android.content.res.Resources$Theme@316183728 (0x12d894b0)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mThemeResource = 2131230884
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   mBase = android.app.ContextImpl@316155392 (0x12d82600)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   shadow$_klass_ = com.qproject.test.leakcanary.LeakCanaryActivity
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: |   shadow$_monitor_ = 1316364430
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: * Excluded Refs:
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Field: android.view.Choreographer$FrameDisplayEventReceiver.mMessageQueue (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Thread:FinalizerWatchdogDaemon (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Thread:main (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Thread:LeakCanary-Heap-Dump (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.WeakReference (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.SoftReference (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.PhantomReference (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.Finalizer (always)
12-25 07:50:51.717 4941-5795/com.qproject.main D/LeakCanary: | Class:java.lang.ref.FinalizerReference (always)
5.获取dump日志文件
如果觉得跟踪信息不足以定位时,DisplayLeakActivity还为开发者默认保存了最近7个dump文件到APP的目录中,可以使用MAT等工具对dump文件进行进一步的分析;
  1. vbox86p:/data/data/com.qproject.main/files/leakcanary # ls  
  2. 2016-12-25_07-50-51_718.hprof 2016-12-25_07-50-51_718.hprof.result  
  3. D:\>adb pull ./data/data/com.qproject.main/files/leakcanary/2016-12-25_07-50-51_ 718.hprof  
  4. [100%] ./data/data/com.qproject.main/f…akcanary/2016-12-25_07-50-51_718.hprof  
vbox86p:/data/data/com.qproject.main/files/leakcanary # ls
2016-12-25_07-50-51_718.hprof 2016-12-25_07-50-51_718.hprof.result
D:\>adb pull ./data/data/com.qproject.main/files/leakcanary/2016-12-25_07-50-51_ 718.hprof
[100%] ./data/data/com.qproject.main/f...akcanary/2016-12-25_07-50-51_718.hprof
Android Studio->View->Tool Windows->Captures,打开Captures窗口,将pull获取的hprof文件剪切到Capture中文件的目录下,双击打开即可;

具体的分析过程,这里就不重点讲述,大家去查询MAT相关的资料;
五、检测其他对象
如果想监听其他的对象(例如Fragment ),可以通过RefWatcher的实例来实现;
 
六、LeakCanary原理
1.RefWatcher.watch()函数会为被监控的对象创建一个KeyedWeakReference弱引用对象,是WeakReference对的子类,增加了键值对信息,后面会根据指定的键key找到弱引用对象;
2.在后台线程AndroidWatchExecutor中,检查KeyedWeakReference弱引用是否已经被清楚。如果还存在,则触发一次垃圾回收之后。垃圾回收之后,如果弱引用对象依然存在,说明发生了内存泄露;

猜你喜欢

转载自blog.csdn.net/Strive_0902/article/details/80208468