如何對 Android 應用進行性能分析
記錄一下自己在使用DDMS的過程:開啓AS,打開並運行項目 找到TOOL/選擇Android Device Monitor
一款 App 流暢與否安裝在自己的真機裏,玩幾天就能有個大概的感性認識。不過通過專業的分析工具可以使我們更好的分析我們的應用。而在實際開發中,我們解決完當前應用所有 bug 後,就會開始考慮到新能的優化。如果不考慮使用其他第三方性能分析工具的話,我們可以直接使用 ddms 中的工具,其實 ddms 工具已經非常的強大了。ddms 中有 traceview、heap、allocation tracker 等工具都可以幫助我們分析應用的方法執行時間效率和內存使用情況。
traceview
TraceView 簡介
Traceview 是 Android 平臺特有的數據採集和分析工具,它主要用於分析 Android 中應用程序的 hotspot(瓶頸)。Traceview 本身只是一個數據分析工具,而數據的採集則需要使用 AndroidSDK 中的 Debug 類或者利用 DDMS 工具。
二者的用法如下:
開發者在一些關鍵代碼段開始前調用 Android SDK 中 Debug 類的startMethodTracing 函數,並在關鍵代碼段結束前調用 stopMethodTracing 函數。 這兩個函數運行過程中將採集運行時間內該應用所有線程(注意,只能是 Java 線程)的函數執行情況,並將採集數據保存到/mnt/sdcard/下的一
個文件中。開發者然後需要利用 SDK 中的 Traceview 工具來分析這些數據。
藉助 Android SDK 中的 DDMS 工具。DDMS 可採集系統中某個正在運行的進程的函數調用信息。對開發者而言,此方法適用於沒有目標應用源代碼的情況。DDMS 工具中 Traceview 的使用如下圖
點擊上圖中所示按鈕即可以採集目標進程的數據。當停止採集時,DDMS 會自動觸發 Traceview
工具來瀏覽採集數據。
下面,我們通過一個示例程序介紹 Traceview 的使用。界面有 4 個按鈕,對應四個方法。
public class MainActivity extends ActionBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void method1(View view) {
int result = jisuan();
System.out.println(result);
}
private int jisuan() {
for (int i = 0; i < 10000; i++) {
System.out.println(i);
}
return 1;
}
public void method2(View view) {
SystemClock.sleep(2000);
}
public void method3(View view) {
int sum = 0;
for (int i = 0; i < 1000; i++) {
sum += i;
}
System.out.println("sum=" + sum);
}
public void method4(View view) {
Toast.makeText(this, "" + new Date(), 0).show();
}
}
我們分別點擊按鈕一次,要求找出最耗時的方法。點擊前通過 DDMS 啓動 Start Method Profiling按鈕。
然後依次點擊 4 個按鈕,都執行後再次點擊上圖中紅框中按鈕,停止收集數據。
接下來我們開始對數據進行分析。
當我們停止收集數據的時候會出現如下分析圖表。該圖表分爲 2 大部分,上面分不同的行,每一行代表一個線程的執行耗時情況。main 線程對應行的的內容非常豐富,而其他線程在這段時間內幹得工作則要少得多。圖表的下半部分是具體的每個方法執行的時間情況。顯示方法執行情況的前提是先選中某個線程。
我們主要是分析 main 線程。
上面方法指標參數所代表的意思如下:
名稱 | 描述 |
---|---|
Name | 該線程運行過程中所調用的函數名 |
Incl Cpu Time | 某函數佔用的CPU時間,包含內部調用其它函數的CPU時間 |
Excl Cpu Time | 某函數佔用的CPU時間,但不包含內部調用其它函數的CPU時間 |
Incl Real Time | 某函數運行的真實時間(以毫秒爲單位),內含調用其它函數所佔用的真實時間 |
Excl Real Time | 某函數運行的真實時間(以毫秒爲單位),不含調用其它函數所佔用的真實時間 |
Call+Recur Calls/Total | 某函數被調用次數及地櫃調用佔總調用次數的百分比 |
Cpu Time/Call | 某函數調用CPU時間與調用次數的比。相當於該函數的平均執行時間 |
Real Time/Call | 同CPU Time/Call類似,只不過統計單位換成了真實時間 |
我們爲了找到最耗時的操作,那麼可以通過點擊 Incl Cpu Time,讓其按照時間的倒序排列。我點擊後效果如下圖:
通過分析發現:紅色部分最耗時
heap
heap 簡介
heap 工具可以幫助我們檢查代碼中是否存在會造成內存泄漏的地方。
用 heap 監測應用進程使用內存情況的步驟如下:
1.啓動 eclipse 後,切換到 DDMS 透視圖,並確認 Devices 視圖、Heap 視圖都是打開的;
2.點擊選中想要監測的進程,比如 system_process 進程;
3.點擊選中 Devices 視圖界面中最上方一排圖標中的“Update Heap”圖標;
4.點擊 Heap 視圖中的“Cause GC”按鈕;
5.此時在 Heap 視圖中就會看到當前選中的進程的內存使用量的詳細情況。
說明:
a. 點擊“Cause GC”按鈕相當於向虛擬機請求了一次 gc 操作;
b. 當內存使用信息第一次顯示以後,無須再不斷的點擊“Cause GC”,Heap 視圖界面會定時刷新,在對應用的不斷的操作過程中就可以看到內存使用的變化;
c. 內存使用信息的各項參數根據名稱即可知道其意思,在此不再贅述。
如何才能知道我們的程序是否有內存泄漏的可能性呢?
這裏需要注意一個值:Heap 視圖中部有一個 Type 叫做 data object,即數據對象,也就是我們的程序中大量存在的類類型的對象。在 data object 一行中有一列是“Total Size”,其值就是當前進程中所有 Java 數據對象的內存總量,一般情況下,這個值的大小決定了是否會有內存泄漏。可以這樣判斷:
1、 不斷的操作當前應用,同時注意觀察 data object 的 Total Size 值;
2、 正常情況下 Total Size 值都會穩定在一個有限的範圍內,也就是說由於程序中的的代碼良好,沒有造成對象不被垃圾回收的情況,所以說雖然我們不斷的操作會不斷的生成很多對象,而在虛機不斷的進行 GC 的過程中,這些對象都被回收了,內存佔用量會會落到一個穩定的水平;
3、反之如果代碼中存在沒有釋放對象引用的情況,則 data object 的 Total Size 值在每次 GC 後不會有明顯的回落,隨着操作次數的增多 Total Size 的值會越來越大,直到到達一個上限後導致進程被 kill 掉。
4、此處以 system_process 進程爲例,在我的測試環境中 system_process 進程所佔用的內存的data object 的 Total Size 正常情況下會穩定在 2.2~2.8 之間, 而當其值超過 3.55 後進程就會被kill。
總之,使用 DDMS 的 Heap 視圖工具可以很方便的確認我們的程序是否存在內存泄漏的可能性。
allocation tracker
allocation tracker 簡介
allocation tracker 是內存分配跟蹤工具
步驟:
運行 DDMS,只需簡單的選擇應用進程並單擊 Allocation tracker 標籤,就會打開一個新的窗口,單擊“Start Tracing”按鈕;然後,讓應用運行你想分析的代碼。運行完畢後,單擊“Get Allocations”按鈕,一個已分配對象的列表就會出現第一個表格中。單擊第一個表格中的任何一項,在表格二中就會出現導致該內存分配的棧跟蹤信息。通過 allocationtracker,不僅知道分配了哪類對象,還可以知道在哪個線程、哪個類、哪個文件的哪一行。