Android OOM 剖析及 LeakCanary


简介 OOM

对于Android 开发会经常遇到一个词,就是OOM,也就是 Out Of Memory 的首字母简写。

出现这个异常的原因,也可想而知了。系统提供的内存有限,而程序想要的很多,超出了系统所能提供的大小,就出现了此异常。

举个栗子

下面举一个经典的例子:


public class SecondActivity extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //模拟内存泄漏
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.d("hahha", "leak");
            }
        }, 10000L);
    }
}

这个例子是创建了 Handler,持有了 Activity,当 Activity被销毁的时候,就会发生内存溢出。

我们除了能够通过 logcat 观察日志,发现出现这个异常之外,还可以在看看看 LeakCanray。

LeakCanray

LeakCanary 是 Square 公司开发的一款内存泄漏自动探测神器!

github网址是:https://github.com/square/leakcanary

下面看看如何集成:

使用 Android Studio 的小伙伴可以在 build.gradle 中添加:

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

这样 debug 和 release 的两种情况下都集成了。

在个人的项目的 application 中:

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


这样的情况下,就将 LeakCanary 初始化完成,并且 debug和 release 的情况下不需要开关来切换。

当项目中出现内存溢出的时候 会在日志中出现类似这样的内容:

12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: In com.wj.leakcanary:1.0:1.
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * com.wj.leakcanary.SecondActivity has leaked:
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * GC ROOT static android.os.Looper.sMainLooper
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * references android.os.Looper.mQueue
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * references android.os.MessageQueue.mMessages
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * references android.os.Message.callback
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * references com.wj.leakcanary.SecondActivity$1.this$0 (anonymous implementation of java.lang.Runnable)
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * leaks com.wj.leakcanary.SecondActivity instance
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * Retaining: 29 KB.
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * Reference Key: 8a80f4a6-d838-4188-b6b5-6ef26bb623c6
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * Device: Genymotion generic Samsung Galaxy Note 2 - 4.3 - API 18 - 720x1280 vbox86p
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * Android Version: 4.3 API: 18 LeakCanary: 1.4 6b04880
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * Durations: watch=5034ms, gc=105ms, heap dump=234ms, analysis=8657ms
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * Details:
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * Class android.os.Looper
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   static $staticOverhead = byte[72]@2764904665 (0xa4cd18d9)
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   static TAG = java.lang.String@2764904904 (0xa4cd19c8)
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   static sThreadLocal = java.lang.ThreadLocal@2765076760 (0xa4cfb918)
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   static sMainLooper = android.os.Looper@2766952392 (0xa4ec57c8)
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * Instance of android.os.Looper
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   static $staticOverhead = byte[72]@2764904665 (0xa4cd18d9)
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   static TAG = java.lang.String@2764904904 (0xa4cd19c8)
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   static sThreadLocal = java.lang.ThreadLocal@2765076760 (0xa4cfb918)
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   static sMainLooper = android.os.Looper@2766952392 (0xa4ec57c8)
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   mLogging = null
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   mQueue = android.os.MessageQueue@2766952424 (0xa4ec57e8)
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   mThread = java.lang.Thread@2764219520 (0xa4c2a480)
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   mRun = true
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * Instance of android.os.MessageQueue
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   mPendingIdleHandlers = android.os.MessageQueue$IdleHandler[4]@2766969784 (0xa4ec9bb8)
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   mIdleHandlers = java.util.ArrayList@2766952512 (0xa4ec5840)
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   mMessages = android.os.Message@2767445304 (0xa4f3dd38)
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   mNextBarrierToken = 12
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   mBlocked = true
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   mPtr = -1185325408
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   mQuitAllowed = false
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   mQuiting = false
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: * Instance of android.os.Message
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   static FLAG_IN_USE = 1
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   static FLAGS_TO_CLEAR_ON_COPY_FROM = 1
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   static FLAG_ASYNCHRONOUS = 2
12-26 04:26:38.505 19038-19383/com.wj.leakcanary D/LeakCanary: |   static sPoolSize = 4
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   static MAX_POOL_SIZE = 50
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   static $staticOverhead = byte[192]@2765071577 (0xa4cfa4d9)
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   static sPoolSync = java.lang.Object@2765094600 (0xa4cffec8)
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   static sPool = android.os.Message@2766957048 (0xa4ec69f8)
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   static CREATOR = android.os.Message$1@2765082744 (0xa4cfd078)
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   target = android.os.Handler@2767087960 (0xa4ee6958)
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   replyTo = null
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   callback = com.wj.leakcanary.SecondActivity$1@2766935080 (0xa4ec1428)
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   data = null
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   obj = null
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   next = null
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   when = 4781012
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   arg2 = 0
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   arg1 = 0
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   what = 0
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   flags = 0
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: * Instance of com.wj.leakcanary.SecondActivity$1
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   this$0 = com.wj.leakcanary.SecondActivity@2767040224 (0xa4edaee0)
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: * Instance of com.wj.leakcanary.SecondActivity
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   mActionBar = null
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   mActivityInfo = android.content.pm.ActivityInfo@2767272368 (0xa4f139b0)
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   mAllLoaderManagers = java.util.HashMap@2767242480 (0xa4f0c4f0)
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   mApplication = com.wj.leakcanary.MyApplication@2766975648 (0xa4ecb2a0)
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   mWindowManager = android.view.WindowManagerImpl@2767030416 (0xa4ed8890)
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   mWindow = com.android.internal.policy.impl.PhoneWindow@2767033512 (0xa4ed94a8)
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   mUiThread = java.lang.Thread@2764219520 (0xa4c2a480)
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   mComponent = android.content.ComponentName@2766959600 (0xa4ec73f0)
12-26 04:26:38.509 19038-19383/com.wj.leakcanary D/LeakCanary: |   mToken = android.os.BinderProxy@2766959736 (0xa4ec7478)
后面还有好些日志,就不粘贴了。


在手机上安装了项目之后,会有一个 logo


点进去之后,可以快速查看出现异常的信息等。方便,效率高。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章