標籤:
本文基本翻譯自Facebook工程師的文章
Speed up your app,也加入了自己的一些內容。
會介紹以下幾個主題
- Systrace
- Traceview
- Memory Profiling
- Allocation Tracker
- GPU Profiling
- Hierarchy Viewer
- Overdraw
- Alpha
- Hardware Acceleration
Systrace
Systrace的功能可以在AS的DDMS中找到,但不太穩定,所以這裡只介紹命令列模式。
另外Systrace只能分析概況,不能定位問題位置,不太感興趣的朋友也可以直接跳到第二節Traceview。
在終端(我的環境是MAC)中先進入sdk/platform-tools/systrace目錄
cd /Users/apple/Documents/Android/sdk/platform-tools/systrace
然後執行Systrace的命令是
python systrace.py --time=10 -a com.duotin.fm -o mynewtrace.html sched gfx view wm
–time=10 表示記錄10秒
更多參數說明請查看官網
執行完開啟產生的mynewtrace.html檔案
呈現這樣的介面
點擊第一列的三角形警示表徵圖或者第二列的圓形警示表徵圖,都可以查看警示詳情,點擊效果分別如下
三角形警示表徵圖,每個表徵圖代表一個警示,點擊後查看警示詳情。
圓形警示表徵圖,每個表徵圖代表一個frame,顯示紅色或者黃色表示此frame的時間已經超過16.6 millisecond per frame的標準,會導致介面失幀。點擊後查看這個frame所有的警示。
此時按”M”快速鍵可以高亮當前選中的frame。“A”和”D”分別為左移和右移視圖,”W”放大,”S”縮小。
此外,點擊各顏色塊,可以查看各顏色塊的詳情。
點擊中的第一個Alert: Inflation during ListView recycling
顯示詳情
可以看出Inflation during ListView recycling的執行時間是32ms(遠遠超過了16ms的限制),共5個item,平均到每個item為6ms。通過點擊該frame範圍內的顏色塊,可以查看各個方法的詳情。
我們再點擊一個圓形警示表徵圖並高亮
頂部顯示此frame耗時19ms, 點擊右下方的Alert,顯示有 Scheduled delay。
Scheduled delay 是指告訴CPU執行任務,但CPU太忙了,任務被順延強制了。
點擊下面的一個顏色塊,顯示如下
Wall duration 是指此顏色塊代表的方法從開始到結束的耗時
CPU duration 是指 CPU的執行時間
可以發現CPU duration只有4ms,但Wall duration有18ms。
延遲這麼嚴重,我們來看看原因。
在選中的顏色塊上方,我們看到四個CPU都被顏色塊填充,表示此時4個CPU都有活幹,很忙。
我們選中一個CPU的顏色塊
可以發現佔用CPU的應用是com.udinic.keepbusyapp
恩,對Systrace的介紹到此結束了,雖然還有些沒講,但Systrace的確只能看個概覽。
且慢,再加送一個tip, 點擊右側邊欄的Alerts, 你能看到所有的Alerts.
通過這張圖我分析出
inefficient view alpha usage數量最多。Inflation during ListView recycling影響時間最長,耗時間長度達52ms
Traceview
Traceview能夠在方法層面上分析APP的效能,非常強大。
可以通過,命令列或者GUI啟動,我用的是GUI啟動,點擊AS的Android Device Monitor, 點擊Devices欄目下面的 Start Method Profilling , 在對話方塊中選擇,(我選的是trace Based Profilling,表示即時分析,會比較慢,但分析結果詳細),操作APP, 分析結束的時候,點擊同一個表徵圖即可。更多操作請訪問官網
先看下介面
| 列名 |
意義 |
| Name |
方法名,每個方法的顏色都不一樣。 |
| Inclusive CPU Time |
此方法佔用CPU的時間,Inclusive指包括調用的方法所 |
| Exclusive CPU Time |
此方法所佔用CPU的時間,Exclusive 指不包括調用的方法 |
| Inclusive / Exclusive Real Time |
Real Time指方法從開始到結束消耗的時間,跟Systrace中的Wall duration一個意思。 |
| Calls+Recursion |
此方法被調用了多少次+多少次是遞迴調用 |
| Calls / Total |
子方法被此父方法調用的次數/子方法被調用的總次數 |
點擊某條目下的parent 或者 child 方法時,會跳到該方法的條目。
想找出最影響效能的方法,可以點擊Exclusive CPU time一欄,找出消耗時間最長的幾個方法。如果是應用的方法,直接看可不可優此方法。如果是系統方法,通過查看其父方法,追溯至應用方法。
而查看子方法,可以看出此方法到底做了什麼。
如果要找UI卡頓的原因,可以從 getView(), View#onDraw() 等方法入手。
方法執行時佔用了CPU,所以執行時間過長會造成UI渲染被延遲,從而應用不流暢。而GC同樣會佔用CPU,AS也同樣提供了查看GC的工具:
Memory Profiling
點擊AS中的Android Monitor, 選中Memory | CPU 一欄, 介面如下
,小幅的記憶體下降一般就是發生了GC。
點擊左側的Heap dump,會產生記憶體中的所有對象的快照。
| 列名 |
意義 |
| Total Counts |
記憶體中該類的對象個數 |
| Heap Count |
在該堆中該類的對象個數,左上方可以選擇App heap或Zygote heap |
| Sizeof |
單個對象佔用的Shallow Size |
| Shallow Size |
所有對象所佔用的Shallow Size |
| Retained Size |
所有對象所佔用的Retained Size,即GC後會釋放的記憶體 |
| instance |
該類一個具體的對象 |
| Reference Tree |
引用這個對象的父物件,點擊父物件,展開這些父物件的父物件 |
什麼是Shallow Size 和Retained Size
選中一行,點擊右側的一個instance,可以在下方看到Refrence Tree介面
在圖中可以看出MemoryActivity的一個instance在ListenerManager中被引用了。如果MemoryActivity已經不在Activity棧中了,這樣的引用就是記憶體流失。另外一個檢查記憶體流失的工具是leakcanary
通過查看Retained Size和Reference Tree,我們可以知道哪些對象佔用了較多的記憶體,對象間的參考關聯性,進而分析是否可以最佳化資料結構,減少參考關聯性,以減少記憶體佔用和GC頻率。
Allocation Tracker
Memory | CPU一欄左側的另一個按鈕Allocation Tracker也是用於分析記憶體佔用。點擊一次表示開始記錄,再次點擊表示停止記錄。
在結果頁面的左上方點擊餅狀圖。
可以選擇 group by Allocator,即按對象劃分。
或者 group by method
Allocator下面的餅狀圖最外圍的是具體的類,內部的是包名。圖中可以看出包或者對象佔用的記憶體大小或者個數,面積越大,佔用或者個數越多。選擇size可以查看佔用記憶體最多的對象,選擇count可以查看以及個數最多的對象。前者我們可以試著最佳化類,後者我們可以嘗試建立一個Object pool來複用對象。
從group by method可以看出,decode方法佔用的總記憶體達10.91M, 就有可能是方法內建立了太多個物件,可以往這方面最佳化。
記憶體方面的tips:
1. Enums Enums比int占的記憶體大得多,而且有替換方案@IntDef, 所以除了某些情況,比如你需要強制指定類型,不然的話int會更節省記憶體
2. 自動裝箱 自動裝箱指從基本類型自動轉換到對象形式的(比如int到Integer),鑒於基本類型使用的情境和次數都較多,所以需要盡量避免使用其自動裝箱的形式。
3. HashMap vs ArrayMap / Sparse*Array 如果我們需要使用int作為Map的值,可以使用SparseIntArray,比起使用HashMap對int自動裝箱,要省記憶體的多。如果要使用Object作為Map的key,除了HashMap,你也可以考慮使用ArrayMap,功能和HashMap一樣,但更省記憶體,點擊瞭解原理。儘管時間效能上HashMap更勝一籌,但除非你要儲存1000個以上對象,否則他們使用起來幾乎一樣快。
4. 注意Context對象 因為Context在開發中的使用情境較多,所以最容易造成記憶體流失。Activity本身是一個heavy的對象,為了避免記憶體流失,可以穿ApplicationContext的話,就不要傳Activity了。
5. 避免非靜態內部類 非靜態內部類隱式持有外部類的引用,所以如果外部類不再被需要,但內部類仍在使用狀態,就造成了記憶體流失。特別是Activity類,在定義內部類的時候盡量定義成static的。
GPU Profiling
首先在手機的開發人員選項頁面,點擊GPU呈現模式分析(Profile GPU rendering),選中“In adb shell dumpsys gfxinfo” 然後在AS的Android Monitor 介面選中GPU一欄,確保左上方的暫停按鈕沒有選中,此時AS就開始按照選定的包名顯示GPU情況了。每一個條直線表示UI渲染中的一幀,不同的顏色表示不同的繪製階段。
- Draw(藍色) 執行的是View#onDraw()方法。這個階段的工作是建立DisplayList對象,這些對象稍後將被轉換成OpenGL命令,傳送到GPU。如果藍色較長,一般是因為較複雜的view, 或者短時間內invalidate了較多的view
- Prepare (紫色) Lollipop才引入的階段,用於加快UI渲染,線上程RenderThread中執行。這個階段的任務是將第一步產生的display lists轉換成OpenGL命令,並傳送到GPU。此時UI thread將繼續處理下一幀。UI Thread給RenderThread傳遞所需的資源產生的耗時也記錄在此階段中。當有大量的資源要傳遞,比如很多/很heavy的display lists,這個階段耗費的時間會增多。
- Process(紅色) 處理display lists,產生OpenGL命令,較多或者較複雜的display lists會使此階段耗時增加,因為很多view將被redraw。view被redraw的情況有invalidate或者之前覆蓋在上面的view現在被移走了。
- Execute (黃色) 將OpenGL命令發送給GPU, 這是個阻塞方法,因為CPU通過buffer將OpenGL命令發送給GPU, 當處理完畢返回空的buffer。buffer的數量有限,所以當GPU很忙,buffer也用完了,CPU就需要等待GPU處理完返回一個空的buffer,才能繼續發送OpenGL命令。因此如果這個階段耗時較多,一般是因為在繪製複雜的view。
在 Marshmallow 版本,增加了更多的顏色
翻譯了一半了,待續
APP效能分析工具