天天看點

中秋一次曆時10小時的記憶體洩露修複

0、序

最近新版本上線,随着使用者增多,發現5.0上的OOM類型的錯誤異常的多,但是4.4上除了一些低端機型都沒有出現同樣的問題,作為一個開發人員怎麼能不找出原因呢?!!

1、工具

對于記憶體洩露這種問題,當然要祭出LeakCanary這種查記憶體洩露的神器啦。

使用的方式也很簡單,用maven就行。使用方法如下,在app的build.gradle下面加入你要用的版本。因為自己項目正式釋出時不會用這個,是以隻用了debug版本的。

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

然後在你的自定義的Application下面加入

public class ExampleApplication extends Application {

  @Override public void onCreate() {
    super.onCreate();
    LeakCanary.install(this);
  }
}      

ok,搞定。但是如果你還想要自己定義監控的頁面或者變量,可以參考 https://github.com/square/leakcanary#how-do-i-use-it 由于當時我的項目中不清楚到底什麼地方洩露了,是以隻做了前面的步驟。

2、思考     由于自己做的是動态圖檔應用,第一反應就是圖檔沒釋放。于是在動圖播放控件detach的時候,全部置為null,但是結果還是記憶體洩露。但是奇怪的是5.0以下的機型不會記憶體洩露,但是5.0及以上的記憶體就死死的釋放不了。當時的另一個想法是系統記憶體洩露了,可參考連結 http://www.ithome.com/html/android/120575.htm

雖然這麼想,但是我覺得作為一個程式猿應該先想到可能還是自己的代碼有問題,而不能盲目的猜測。

于是用LeakCanary一查,發現是動圖播放控件沒有釋放,一環接一環的影響了Activity的釋放,最終記憶體洩露了。但是一看自己的代碼,不管在detach或者切換時都已經做了釋放,不可能出現未釋放的情況。

接下去,我就在recyclerView裡面找原因,覺得會不會是recyclerView并沒有detach掉清單項,因為之前找到了很多個recyclerView的bug,于是乎就列印log查找是否detach,但是發現每一項都被detach了。同時我注意到另一個問題,當我隻是慢速滑動的時候,LeakCanary并不顯示記憶體洩露,而是在我快速滑動的時候,LeakCanary就顯示記憶體洩露了。我想會不會是快速滑動導緻detach失效呢?但并不是這樣的,在快速滑動過程中,每一項都有被bind到,也同時會被detach。

由于之後沒有頭緒,甚至就試着關閉快速滑動。陽台上走了一圈,突然靈感迸發,如果是記憶體洩露,會不會是線程沒有停止,于是就檢視lib,然後列印日志,果然在快速滑動下線程并沒有被關閉,而是一直在輸出log日志。因為線上程中持有了一個Handler對象,Handler對象又被控件所持有,結果就惡性循環導緻記憶體沒法釋放。但是改成WeakReference的Handler,發現也存在洩露問題,說明這個線程在其他地方被啟動了。查找了app module下所有啟動的地方,發現并沒有不符合規範的地方,後來又去lib下面查找,才發現在圖檔下載下傳完成之後竟然直接就啟動線程播放了。。。。。于是把線程開始全部改成代碼控制。

3、結論

不要在一個線程結束後有緊接着調用一個死循環的線程,除非你有特定的條件去觸發線程停止,否則最好還是在可控的範圍内調用線程,畢竟這東西是不可控的,就算你退出程式,還是會運作。

Over 。