簡介 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);
}
}
當項目中出現內存溢出的時候 會在日誌中出現類似這樣的內容:
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
點進去之後,可以快速查看出現異常的信息等。方便,效率高。