Android 應用性能測試——內存篇

Android 應用性能測試——內存篇

Android內存監控與分析三部曲(一)--最常遇見的內存泄漏測試場景

Android內存監控與分析三部曲(二)--Java內存管理機制

Android內存監控與分析三部曲(三)--Android的內存管理

APP測試中難免會有各種顯式或者隱式的內存泄漏(Memory Leak)問題,如果不及時發現處理,可能會因爲內存泄漏導致各種奇怪的問題(如,卡頓和閃退),甚至可能出現因內存不足(Out of Memory,簡稱OOM)而導致APP崩潰。

本文分爲三部分,通過實戰分析內存泄漏和內存溢出問題,並在必要時說明原理或機制。三部曲快速準確定位常見的內存問題。即,

(一)內存泄漏與內存溢出的表現形式和最常遇見的內存泄漏測試場景

(二)分析內存泄漏的原理

(三)內存分析實例演示

圖1 內存監控與分析

 

內存分析實例演示

一、內存測試流程中的要點

1. 代碼

通常用來進行內存測試的版本是純淨版本,不應該附加多餘的Log和調試用組件。例如有些情況下,爲了測試界面延遲/函數執行時間等性能,會加入一些樁點代碼。在內存測試中這些代碼是不必要的,它們可能會分配臨時內存,引起更多的GC,導致應用出現運行緩慢、卡頓等現象。

2. 測試場景

測試場景通常有兩類。

一類是當前有新開發或改動的某項功能,需要對該功能進行性能測試。因此測試場景主要針對該功能組織,包括功能的開啓前、運行、結束後等測試點。

另一類是整體性能,考察應用的常見場景,在綜合使用情況下的性能指標。測試場景應當包括啓動後待機,切換到後臺,執行主要功能,以及反覆執行各功能後。

在各類場景中,經常作爲測試重點的有:

在各類場景中,經常作爲測試重點的有:

  • 包含了圖片顯示的界面。

  • 網絡傳輸大量數據。

  • 需要緩存數據的場景。

3. 場景轉換成用例

選取了測試場景後,用例設計也要考慮內存測試的特點。一些常見的方法是:

  • 結合場景比較操作前後或不同版本的內存變化。

  • 顯示多張圖片的前臺進程。

  • 多個場景來回切換。

  • 長時間運行進程的內存增長。

4. 執行

由於GC和廣播機制的存在,應用內存通常都在不停地波動,幅度可能會達到幾百KB,因此執行時需要考慮這種情況。在採集數據時,需要多次採集並計算平均值。

執行完成,我們就可以根據數據進行比較初步的分析以確定方向。一方面是我們熟悉的Dalvik Heap部分,即由Java代碼直接分配的內存,可以通過IDE直接觀察到使用情況,也可以使用MAT進行細緻的分析。

另一方面,假如我們發現Dalvik Heap沒怎麼增長,而其他部分增長了許多,這種情況下的分析就要複雜一些。

排除方法通過logcat命令輸出的log信息,搜關鍵詞“GC”。如果有下面四個中的一個,就可能存在內存泄露。

GC_FOR_ALLOC:因爲在分配內存時內存不夠引發的;

GC_EXPLICIT:表明GC被顯式請求觸發的,如Runtime.gc()或VMRuntime.gc()調用;

GC_CONCURRENT:表明GC在內存使用率達到一定的警戒值時,自動觸發;

GC_BEFORE_OOM:表明在拋出內存溢出(OOM)異常之前,嘗試執行最後一次內存回收。

二、實戰演示(TestMemory.apk實例)

Java堆用於存儲對象實例,我們只有不斷地創建對象,並且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,就會在對象數量到達最大堆的容量限制後產生內存溢出異常。

圖五、六操作導致的內存泄漏部分代碼如下:

 

在一個MainActivity,實現了listeners。 這個MainActivity中,addMemory.setOnClickListener創建一個大的字符串數組,不斷點擊此按鈕導致內存泄漏;持續點擊直到內存溢出。在跳轉頁面clickToSecondPage.setOnClickListener後,staticActivity 引用MainActivity導致內存泄漏。

分析TestMemory內存泄漏任務包括:

1) 檢測泄露的Activity實例

2) 查找重複的String實例

HPROF Viewer界面,分析內存泄漏。如圖20:

圖20 內存泄漏分析

第一步➡️

點擊“Analyzer Tasks”視圖中的啓動按鈕,啓動分析。

第二步➡️

查看“Analysis Result”中的分析結果,點擊“Leaked Activities”中的具體實例,該實例的引用關係將會展示在“Reference Tree”視圖中。

第三步➡️

根據“Reference Tree”視圖中的引用關係找到是誰讓這個leak的activity活着的,也就是誰支配(Dominate)這個activity對象。

此例中, 比較簡單,可以很清晰看到是this的實例最終支配了MainActivity。“this”實例連接到GC Roots,故而導致MainActivity GC Roots可達,無法被回收。

上述步驟, 可以讓我們快速定位可能的內存泄露。 當然,內存問題,除了內存泄露,還有內存消耗過大。我們可以在Heap Viewer中查看分析內存的消耗點。

使用Heap Viewer查看內存消耗。如圖21:

圖21 Heap Viewer查看內存消耗

第一步➡️

點擊視圖中“Package Tree View”的按鈕, 使用包視圖,可以讓我們關注到自動APP相關對象的實例。

第二步➡️

點擊“Retained Size”排序, 快速找到內存消耗點。

第三步箭頭

找到最大內存消耗點。

總結

分析內存問題, 主要是觀察和比較內存增長情況。然後,分析對象的內存佔用(Retained Size)情況,找出Retained Size較大的對象,找到其直接支配者(Immediate Dominator),跟蹤其GC可達路徑(Path to GC Roots),從而找到是誰讓這個大對象活着。

 

 

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