Android - 通過真實案例學習解內存泄漏問題,最終發現Android原生Bug


  作為一個Android新手小白,剛到新公司,最近的工作就是在學習解各類Bug。轉型之初,面臨各種新知識,會有壓力,但是學習的過程是快樂的。

  上周剛遇上一類bug,就是應用的內存泄漏問題。最終通過前輩的指點,用了兩天的時間(包括今天),來解決了這個問題,並最終發現了Android原生代碼的bug(值得開心......)。因此將學習的過程總結出來,可以供像我一樣的新人參考學習。

 

一. 問題發現的背景

   QA測試發現,多次打開Android系統中設置功能里的某個Activity時,其占用的資源未能釋放,並且在兩三百次的重復操作后,設置應用發生了Crash的現象。

   崩潰的原因是OOM問題,即占用的資源因未能被GC回收,導致內存不足,拋出了OOM(Out of Memory)的異常,應用發生Crash。

   因此下一步就是RD來解決問題啦!

 

二. 需要准備/掌握的工具

   沒有工具的配合,你很難輕松的應對和解決問題。經過前輩的指導后,開始入手學習使用解此類問題的一系列工具。

      1. Adb Shell 命令

     Android新手入門一定先從Adb開始,Adb全稱是Android debug bridge,提供很多操作手機的命令,有了它,可以方便的debug問題。這里我們使用的命令如下,

     A. adb shell

             進入adb shell,執行以下命令

     B. am start -a "xxx" -d "xxx"

             通過activity manager打開activity,方便多次測試,調查進程內存占用情況

     C. dumpsys meminfo xxx

             查看進程的內存占用情況,xxx為包名

   2. DDMS + MAT工具

       DDMS全稱是Dalvik Debug Monitor Service,一般我用它來查看即時log,這里的作用是使用DDMS來生成hprof文件,hprof是Android進程的heap快照,有了它,可以來研究heap中存在哪些object,以及object的引用,研究為何GC沒有回收對象的原因。

       而MAT工具,正是由Eclipse提供的,能方便分析hprof文件的工具。MAT全稱是Memory Analyzer Tool,內存分析工具,安裝方式是在Eclipse中,選擇install new software,然后提供插件的網址,選擇安裝即可。

   因此這里我們的思路是,通過Adb shell命令來測試並重現問題,然后用DDMS來抓取heap快照,使用MAT來分析heap快照,從來對照代碼解決問題。

 

三. 解決此內存泄漏問題的過程

   1. 重現問題,通過am start命令直接打開此Activity,然后按手機的返回鍵,多次重復此過程

   2. 在步驟一的過程中,每次都使用dumpsys meminfo com.android.settings命令,來觀察heap中Activity的數量。

     正常的情況下,Activity的值應該為0或1,不應該持續增長,因為按返回后,如果不存在內存泄漏,無用的Activity對象會被GC(垃圾回收)給回收掉。

     但這里,因為有問題存在,我觀察到的現象是,Activity的數量一直在增長。如下圖所示,heap中Activity的數量變化:

     步驟一操作1次,

         

         操作2次,

         

         操作5次,

         

         可以明顯的看到問題的發生,即在我們每次操作過程中,Activity雖然已經通過返回鍵,不予顯示,但是占用的資源未能被GC回收,每次操作都會生成一個新的不會且不會被釋放的Activity對象,發生了內存泄漏!

         因此下一步要來解決問題。

   3. 使用DDMS+MAT發現線索,解決問題

         既然現場已經重現,此時我們需要用DDMS來生成hprof文件,這里提到一點,如果你使用的都是Eclipse里安裝的DDMS與MAT工具,在DDMS中點擊生成hprof文件,會自動關聯MAT,使用MAT打開此文件。

         DDMS生成hprof文件,點擊下圖中的2個綠色按鈕,如下,

         

          MAT打開hprof文件,打開時建議選擇第一項,如下,

          

          之后打開后,就能分析heap文件啦。這里我們選擇,點擊Dominator Tree,它能列出heap中最大的對象們,

          

           然后在打開的頁面中,選擇你測試時發現問題的Activity(可以使用關鍵詞來過濾結果),這里出問題的Activity是,AppDrawOverlaySettingsActivity(Android原生代碼),其對應的Fragment是DrawOverlayDetails。由於我們操作了5次,可以發現heap中的5個對象存在,都沒有被釋放。

   

         這時要分析其未被釋放的原因,要使用到MAT的功能來分析對象的引用,因為強引用的對象不會被GC回收。既然這個Activity對象一直存在,就說明一定是有引用存在,導致其未被GC回收。

     我的做法是,右擊object,點擊Merge Shortest Paths to GC Roots -> exclude all phantom/week/soft etc. preferences(因為要排除弱引用,以及軟引用,這些引用包含的對象都會被GC回收,對我們沒有參考價值)。

     之后便可以發現原因了,

          

     通過查看其引用,發現存在一個可疑的mSession變量,它屬於Activity的父類,在類中使用了當前的對象,但是一直未能釋放,因此這就是問題的原因,導致GC未能回收資源。

     知道原因后,解決的方法便很簡單,就是在按返回鍵,觸發的onStory或onDetach方法中,釋放此mSession對象。最終便能解決問題。

1    @Override
2     public void onDestroy() {
3         super.onDestroy();
4         mSession.release();
5     }

          

最后提交修改,在新的apk測試中,通過Adb shell命令測試發現,Activity數量已維持正常,內存泄漏的問題便也已解決。

 

最后總結,解決內存泄漏的問題,熟練使用命令和工具很重要。有了它們的幫助,能快速的找到線索,再到代碼中去發現問題。當然復雜的問題,遠沒有本文中解決的過程簡單,但是對於新手來說,學習此方法步驟會有很大幫助!

 

                                    - Kevin Song

                                    2016年5月9日

 

                 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM