LeakCanary: 查找内存泄露

什么是内存泄露

一些对象有着有限的生命周期。当这些对象所要做的事情完成了,我们希望他们会被回收掉。但是如果有一系列对这个对象的引用,那么在我们期待这个对象生命周期结束的时候被收回的时候,它是不会被回收的。它还会占用内存,这就造成了内存泄露。持续累加,内存很快被耗尽。

比如,当 Activity.onDestroy 被调用之后,activity 以及它涉及到的 view 和相关的 bitmap 都应该被回收。但是,如果有一个后台线程持有这个 activity 的引用,那么 activity 对应的内存就不能被回收。这最终将会导致内存耗尽,然后因为 OOM 而 crash。


对战内存泄露

排查内存泄露是一个全手工的过程,这在 Raizlabs 的 Wrangling Dalvik 系列文章中有详细描述。

以下几个关键步骤:

  1. 通过 Bugsnag, Crashlytics 或者 Developer Console 等统计平台,了解 OutOfMemoryError 情况。

  2. 重现问题。为了重现问题,机型非常重要,因为一些问题只在特定的设备上会出现。为了找到特定的机型,你需要想尽一切办法,你可能需要去买,去借,甚至去偷。 当然,为了确定复现步骤,你需要一遍一遍地去尝试。一切都是非常原始和粗暴的。

  3. 在发生内存泄露的时候,把内存 Dump 出来。具体看这里。

  4. 然后,你需要在 MAT 或者 YourKit 之类的内存分析工具中反复查看,找到那些原本该被回收掉的对象。

  5. 计算这个对象到 GC roots 的最短强引用路径。

  6. 确定引用路径中的哪个引用是不该有的,然后修复问题。

    很复杂对吧?

    如果有一个类库能在发生 OOM 之前把这些事情全部都搞定,然后你只要修复这些问题就好了,岂不妙哉!

    LeakCanary

    LeakCanary 是一个检测内存泄露的开源类库。你可以在 debug 包种轻松检测内存泄露。

    用法

    监控 Activity 泄露

    我们经常把 Activity 当作为 Context 对象使用,在不同场合由各种对象引用 Activity。所以,Activity 泄漏是一个重要的需要检查的内存泄漏之一。

    public class ExampleApplication extends Application {
    
        public static RefWatcher getRefWatcher(Context context) {
            ExampleApplication application = (ExampleApplication) context.getApplicationContext();
            return application.refWatcher;
        }
    
        private RefWatcher refWatcher;
    
        @Override public void onCreate() {
            super.onCreate();
            refWatcher = LeakCanary.install(this);
        }
    }

    LeakCanary.install() 返回一个配置好了的 RefWatcher 实例。它同时安装了 ActivityRefWatcher 来监控 Activity 泄漏。即当 Activity.onDestroy() 被调用之后,如果这个 Activity 没有被销毁,logcat 就会打印出如下信息告诉你内存泄漏发生了。

        * com.example.leakcanary.MainActivity has leaked:
        * GC ROOT thread java.lang.Thread.<Java Local> (named 'AsyncTask #1')
        * references com.example.leakcanary.MainActivity$2.this$0 (anonymous class extends android.os.AsyncTask)
        * leaks com.example.leakcanary.MainActivity instance
        * Reference Key: c4d32914-618d-4caf-993b-4b835c255873
        * Device: Genymotion generic Google Galaxy Nexus - 4.2.2 - API 17 - 720x1280 vbox86p
        * Android Version: 4.2.2 API: 17
        * Durations: watch=5100ms, gc=104ms, heap dump=82ms, analysis=3008ms

    监控 Fragment 泄漏

    public abstract class BaseFragment extends Fragment {
    
        @Override 
        public void onDestroy() {
            super.onDestroy();
            RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
            refWatcher.watch(this);
        }
    }

    当 Fragment.onDestroy() 被调用之后,如果这个 fragment 实例没有被销毁,那么就会从 logcat 里看到相应的泄漏信息。

    监控其他泄漏

        ...
        RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
        refWatcher.watch(someObjNeedGced);

    当 someObjNeedGced 还在内存中时,就会在 logcat 里看到内存泄漏的提示。

    集成 LeakCanary 库

    dependencies {
        debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
        releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
    }

    如果出现内存泄露,则在log里会显示:

        * com.example.leakcanary.MainActivity has leaked:
        * GC ROOT thread java.lang.Thread.<Java Local> (named 'AsyncTask #1')
        * references com.example.leakcanary.MainActivity$2.this$0 (anonymous class extends android.os.AsyncTask)
        * leaks com.example.leakcanary.MainActivity instance
        * Reference Key: c4d32914-618d-4caf-993b-4b835c255873
        * Device: Genymotion generic Google Galaxy Nexus - 4.2.2 - API 17 - 720x1280 vbox86p
        * Android Version: 4.2.2 API: 17
        * Durations: watch=5100ms, gc=104ms, heap dump=82ms, analysis=3008ms

    真是一个非常好用的工具


    参考:http://www.jianshu.com/p/0049e9b344b0

                 http://www.liaohuqiu.net/cn/posts/leak-canary/


你可能感兴趣的:(Android)