文章出處

     用戶使用移動產品時,App 崩潰現象會嚴重影響用戶體驗。而Android 低端機器正是崩潰的重災區,經常會拋出OOM(out of memory)異常。究其原因多數是App 使用過程中發生內存泄漏,因此為了提高App 的穩定性,測試過程中需要反復操作嘗試定位此類問題。本文將針對Android 設備崩潰產生的常見場景和定位工具進行闡述。

 

Android 內存泄漏的常見場景

     Android 程序多數基于Java 語言開發。Android 系統在運行時,會給每個應用分配一個Dalvik 虛擬機作為進程運行的基本單位。與Java 中的JVM 虛擬機類似,Dalvik 也實現了虛擬機中的垃圾回收功能,以保證資源的合理使用。內存泄漏行為就是指App 使用過程中沒有按正常邏輯被GC 回收的對象。因此Java 程序常見的內存泄漏場景在Android 中同樣適用,本文主要描述Android 特有的內存泄漏現象的場景,從而方便測試重現此類問題。

 

1. 頁面的銷毀與建立

       場景:多次關閉、打開同一頁面后,觸發GC,內存增長,可能存在崩潰。

       原因:Android 應用頁面之間頻繁的切換和繪制,可能會存在某些頁面對象無法被GC回收,大量的相關對象停滯在Heap 區,從而導致內存泄漏。 舉例來說為了加快頁面渲染速度,經常會使用靜態變量緩存頁面背景。但是如果沒有正確處理,在SDK-10 以下的系統上,會導致頁面背景對象持有控件對象的引用,使整個Activity 都無法被GC 回收,多次重開同一個頁面后,內存就會飆升了,而SDK-15 以上已經通過WeakReference 解決了這一問題。

 

2. 資源對象

       場景:

       a. Android 中常使用廣播broadcast 來監聽系統事件,如果注冊對象后,未關閉注冊,假如監聽事件是很頻繁的操作(如鎖屏等),容易造成OOM。

       b. 數據庫游標,文件IO 流等沒有及時關閉。這種場景需要在大量操作后,才會有明顯的內存異常現象,很容易被忽略。

       原因:資源對象未及時回收或關閉,從而導致內存泄漏

 

3. 圖片

       場景:大量圖片的加載和切換

       原因:圖片資源相對于其他資源都較大,雖然系統能夠確認Bitmap 分配的內存最終會被銷毀,但是由于它占用的內存過多,所以很可能會超過堆的限制,直接導致OOM,而崩潰產生的原因有以下2 點:

       a. 使用完成后,沒有及時的銷毀。

       b. 總是保留原圖大小的對象。如果圖片實際的顯示區域較小,可以設置合適的采用率保存Bitmap 對象,減少大圖的內存占用。

 

4. 橫豎屏切換

       場景:橫豎屏切換

       原因:橫豎屏切換時,會對頁面元素進行重繪,如果處理不當,Handler、Thread 等的數量會隨著頁面的重建次數增加而增加,內存泄漏問題就很容易被放大。

 

5. 列表的滑動與重載

       場景:App 中很多內容都會通過列表的形式來展示,包括圖片的瀏覽等,列表滑動和重載的過程可能會導致內存泄漏,列表中包含大量圖片時,不必要的內存開支會導致內存資源不足。

       原因:列表在滑動時,不可見的item 對象會被回收,而被用來構造新的item,若在構造每一個item 時,沒有使用緩存的convertView,會造成內存垃圾。當快速滑動時,容易給垃圾回收較大壓力,如果GC 來不及清理資源,虛擬機不得不分配更多內存來使程序正常運行。

 

 

Android 內存泄漏分析方法

     既然了解了Android 內存泄漏可能出現的場景,那么接下來介紹具體的工具來輔助我們進行內存泄漏分析。Android tools 中自帶的DDMS 就是一個很實用的內存檢測工具,可以動態查看進程的heap 信息,跟蹤內存分配情況以及線程狀態,通過與內存分析工具MAT 的結合,可以方便的監控分析潛在內存問題。

  

1. DDMS 跟蹤進程的heap信息

       DDMS 工具可以在eclipse adt 插件或SDK tools 中打開。通過DDMS 的Devices 面板,可以查看設備運行中的進程,選中進程后,點擊工具欄的Update Heap,即可開始該進程的heap 信息監控,如圖1。

       (Tips:如果使用安卓真機,那么只能看到本地Eclipse 開啟Debug 開關編譯到真機上的App 進程,要想看到所有的進程,那么刷成開發機吧。)

       上文場景中,提到了Bitmap 使用不當時,容易引發內存泄漏,這里就以某產品的漫畫功能作為實例。漫畫頁面占用內存的主要對象即為Bitmap !在DDMS 的Heap 面板里,通過Cause GC 可以觸發GC,實時查看到當前的堆情況。圖2 展示了一次GC 后的Heap 情況,系統分配的堆控件Heap Size 和應用程序實際占用的內存Allocated 已經增大到異常數量級,其中byte array 占用了大部分的空間。如果觀察動態數據,可以看到在漫畫翻頁的過程中,每次GC 后,堆的大小一直增加,沒有明顯的回落,因此有內存泄漏的可能性。

       

       當然圖2 中Heap 的泄漏可能性比較明顯,測試中也可以嘗試通過下面兩種方法來起到放大泄漏問題的效果:

       a. 在不同App 間多次切換。可以通過Android 的Home 鍵,或者歷史進程鍵來切換App,使App 在不同的生命周期內不斷變化,通過不斷的喚醒onResume 和onPause 暫停頁面時,頁面對象處理不當引發的泄漏問題,會進一步暴露出來。

       b. 多次切換橫豎屏。橫豎屏切換經常可以引發App 中Activity、Context、View 等對象的泄漏,因為橫豎屏切換會使頁面重新加載。如果App 在其他代碼中保持了對上述對象中某一個的引用,系統GC 將無法回收重載過程中本應該回收的資源,Heap 會有明顯的增加。

       既然監控發現了可能的內存泄漏問題,可以借助hprof 文件來分析泄漏的根源所在。回到Devices 面板,點擊Dump HPROF file,即可導出當前GC 后Heap 的詳細信息。當然并不是所有內存泄漏都能由OOM 或者Heap Size 的變化來檢測,需要進一步分析Heap 的具體使用情況才能明確問題所在。

 

2. MAT 分析內存泄漏原因

       HPROF 文件可以使用Java 常用的內存分析工具MAT 來分析,其中Dominator Tree 和Histogram 應該是最有用的工具。在圖3 的dominator_tree 列表中,com.netease.x.x.x 直接或間接引用到的Retained Heap 相當大,再逐層查看,發現存在30 多個Bitmap 對象沒有被GC 回收。

 

 

       通過Histogram 也可以看到byte[] 占用了大部分的Shallow Heap(這是由于Android3.1之后,Bitmap 像素數據的內存分配在Dalvik Heap 中), 如圖4。選擇Path To GC Roots->exclude weak references,可以看到對象的逐層引用關系,找到內存泄漏的根源。

 

       在移動產品Android 端的測試過程中對Heap 和GC 信息的監控是必要的,通過這些宏觀的監測數據,發現疑現象,深入分析找到內存泄漏的根源,對于解決崩潰現象,提高測試產品的穩定性,有很大的幫助。

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()