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


點進去之後,可以快速查看出現異常的信息等。方便,效率高。

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