正确姿势使用TraceView工具
?? ?? ?? ? 正確姿勢使用TraceView工具
?? 在對手機應用性能分析和定位的過程中Traceview是使用最多的一個工具,在遇到啟動時間長界面切換時間長特別卡頓的時候Traceview是首選工具。如果查看界面的幀率問題建議還是先使用GPU配置文件以列表的形式展示在屏幕上這樣可以首先發現這個界面的幀率是否有問題再做后續的排查。
如何開啟TraceView
?? Traceview是Android平臺特有的數據采集和分析工具它主要用于分析Android中應用程序的性能問題。Traceview本身只是一個數據分析工具而數據的采集則需要使用Android SDK中的Debug類或者利用DDMS工具。二者的用法如下:
(1) 通過代碼開啟:
就是使用如上代碼方法,當運行了這段代碼的時候,就會有一個trace文件在/sdcard目錄中生成,也可以調用startMethodTracing(String traceName) 設置trace文件的文件名,最后你可以使用adb pull /sdcard/test.trace /tmp 命令將trace文件復制到你的電腦中,然后用DDMS工具打開OK了。
(2)通過DDMS工具
?? 借助Android SDK中的DDMS工具。DDMS可采集系統中某個正在運行的進程的函數調用信息。對開發者而言此方法適用于沒有目標應用源代碼的情況(且必須應用源代碼沒有設置android:debuggable=“false”,否則也是望洋興嘆,無能為力了,可以修改固件另外一說 啊)。DDMS工具中Traceview的使用如圖所示,主要是使用"Start Method Profiling"按鈕進行相關的操作:
在做性能分析的過程中基本都是采用DDMS工具中來啟動TraceView這樣簡單易用隨便哪個地方的代碼都可以跟蹤。在對android 4.4以上手機點擊TraceView按鈕的時候會出現2種方式的選擇對話框如下所示:
(1)、Sample based profiling以固定的頻率像VM發送中斷并搜集調用棧信息。低版本手機也是采用該方式來采集樣本的默認是1毫秒采集一次。精確度和采集的頻率有關間隔頻率越小會越精確但運行也會相應的更慢。
(2)、Trace based profiling不論多小的函數都會跟蹤整個函數的執行過程所以開銷也會很大。運行起來會非常的慢不適合檢測滑動性能。
一般情況用第一種默認1000微妙的間隔就足夠了。
另外在Eclipse中或者Android Studio中啟動的DDMS中的這個工具搜索功能不能使用如果要使用搜索功能可以通過Android SDK tools下的命令行來啟動這樣就可以搜索了。
TraceView面板介紹
?? 通過前面的篇章,我想讀者朋友們一定對TraceView的功能和啟動方式有了一定的了解,那么下面我們來介紹一下其面板和及其功能,Traceview其UI劃分為上下兩個面板即Timeline Panel和Profile Panel,下面分別進行介紹:
Timeline Panel:左邊是測試數據中所采集的線程信息右邊Pane所示為時間線時間線上是每個線程測試時間段內所涉及的函數調用信息。內容的豐富代表該時間段執行的函數多從而可以反應線程的繁忙狀態。也可以看出線程的啟動時間和結束時間等。
Profile Panel是Traceview的核心界面其內涵非常豐富。它主要展示了某個線程先在Timeline Panel中選擇線程中各個函數調用的情況包括CPU使用時間、調用次數等信息。而這些信息正是查找性能瓶頸的關鍵依據。
另外開發者可以在時間線Pane中移動時間線縱軸。縱軸上邊將顯示當前時間點中某線程正在執行的函數信息。
另個面板之間也是互相聯動的點擊下面的函數可以在時間軸上顯示對應的位置。如上圖。點擊時間線上的函數位置則可以展開對應Profile Panel的函數行數。在時間線上拉伸可以放大時間線雙擊頂部的時間條區域可以縮小會原始狀態。
點一個方法后可以看到有兩部分,一個是Parents,另一個是Children。
-
Parent表示調用這個方法的方法,可以叫做父方法
-
Children表示這個方法中調用的其他方法,可以叫做子方法
Profile Panel中各列的含義。如下表所示:
| 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類似只不過統計單位換成了真實時間 |
TraceView實際案例分析
?? 了解完Traceview的基本信息后現在介紹如何利用Traceview來查找性能問題。讓我們來浪一把,分析分析各種問題。
1、直接查看幀率和渲染情況
如上面的TraceView圖面板所示顯示了Draw函數的執行情況。在時間線上可以看到前面部分間隔平滑且時間比較短到中間部分開始就開始中斷執行時間也明顯拉長。說明出現了丟幀等情況通過放大時間線可以查看執行時間較長的draw函數中每一個函數的執行情況從而發現問題。
2、了解函數前后的路徑和執行情況
Profile Panel面板的函數有Parents和Chindren對于部分有遞歸調用的函數還會有Parents while recursive和Chindren while recursive。通過點擊Parents和Chindren的各個函數用于跟蹤性能問題也能了解這個函數的來龍去脈。以及幾個Parents調用了該函數每個Parents調用的次數。該函數自己的執行時間以及各個Chindren和他們的執行時間以及本函數Chindren中用到的某個函數A占據所有調用該函數A的分布比例情況。
3、找出函數所在的線程分布
Profile Panel面板的函數點擊后在對應上面部分函數時間線上會有相應的指示如下面的括號上下的顏色標記符號該顏色標記符號和Profile Panel面板中的該函數前面的方塊顏色對應。如下圖Logger.isLoggable函數點擊后從時間線上看到了在main線程和Thread-3191線程中的分布情況。
4、CPU定位高負荷函數
通過Incl Cpu Time排序就可以輕松發現cpu被哪些函數占用了。
)
5、查找主線程耗時
通過Incl Cpu Time排序可以找到相對耗時的函數在函數排序的面板中選中該函數如果在Main線程中底部某區間出現了括號則表示該線程這段時間執行了該函數。這樣就可以找出主線程的耗時函數了。同理也可以查找某函數在各個線程中的分布情況。
6、查看部分GC原因和位置
因為安卓2.3以后GC并不會每次都停止其他線程因此只能跟蹤到部分停止所有線程的GC情況。一般出現GC的時候時間線上會有比較大塊的同顏色的區域點擊后就可以定位到函數面板區域的GC函數一步一步向parent函數追蹤就可以定位到GC的起因了。如下圖的綠色部分主線程在加載資源圖的時候發生了GC。
7、動畫或者滑動過程是否觸發Layout
動畫和滑動過程中在控件調用gone或者動態添加刪除重新設置paramsTextView重新設置文字以及重新設置Drawable的時候都會觸發Layout。在ListView的getview過程中它自己阻斷了這個requestlayout自己對子控件做了layout的操作所以不會引起整個界面的重新布局。但是如果在其他時間設置了圖片、文字等就可能導致requestlayout被觸發進而執行onMeasure過程和onLayout過程這樣的話就會大大影響了滑動過程中的性能容易造成卡頓。在滑動過程中或者有動畫的情況下做TraceView跟蹤可以發現是否被觸發了重新布局。在跟蹤結束中搜索onLayout或者layout或者requestlayout可以方便找到對應的控件。
8、找出較小的耗時函數
前面按照Incl Cpu Time排序一下就可以找到較大的性能問題函數但是小的耗時函數就不是通過這種方式來找了。我們把Call+Recur Calls/Total和Cpu Time/Call放到最前面按照Cpu Time/Call排序找出平均執行時間久的函數展開其子函數分析是否存在問題并通過調用次數看嚴重的程度。如下圖我們發現社區界面在滑動過程中的TBS提交埋點函數耗時過久進一步跟蹤發現是Hashmap多余的putall操作。
9、查找高頻率調用存在性能的點
我們把Call+Recur Calls/Total和Cpu Time/Call放到最前面按照Call+Recur Calls/Total排序查看執行次數多的隱患函數展開其子函數分析是否存在問題并通過Incl Cpu Time 的CPU占用比以及Incl Cpu Time 的占用百分比來判斷嚴重性特別是調用次數多的且Cpu Time/Call次數也多的應該重點排查。通過這樣我們就能找到高頻率調用函數的性能問題點。我們發現一個簡單的函數但是調用次數太多后導致了相對的耗時且這里只要用到一個寬度只要第一次獲取后保存該值不需要每次從系統函數中去取這樣就解決了。
但是有時候判斷一個函數是否嚴重還是需要對系統的了解。比如SharedPreferences的apply函數較高頻率調用但是其CPU和單次時間都不會占用多少但是這確是一個性能影響點因為直接commit有阻塞的IO操作apply函數調用后進程中有專門一個SharedPreferences的寫線程會處理寫入操作而這個寫線程此時可能會很耗時。反過來如果看到SharedPreferencesImpl&*run線程占用較高cpu的時候就可以推斷出較多的SharedPreferences的操作了我們應該通過搜索把apply的調用出都找出來。
10、查看布局性能問題
通過Incl Cpu Time百分比排序列表滑動過程中如果看到onMeasure或者onLayout大于25%以上的就應該可以判斷出當前這個界面的布局性能不佳需要優化了。
在列表滑動過程中也需要檢查getview這樣的函數的性能特別是布局復雜的初始化時間會比較久。
11、查看布局復用問題
在列表滑動的過程中或者廣告Banner控件一般的做法都是應該復用布局提升性能的但有時候因為覺得麻煩有些可能是動態添加的就沒有復用這些view導致在滑動過程中還是會出現infalte布局的情況影響性能。跟蹤方法是在這個列表已經滑動過的情況下開始進行TraceView這個時候來回滑動不應該出現infalte如果出現了就是復用出現了問題。下圖中我們對“我的訂單”界面做了跟蹤發現有動態inflate button導致每次都額外增加了時間影響性能。
還有一種判斷方法就是在進入界面的時候找出LayoutInflater.createViewFromTag函數找出它數量以及parents調用方檢查是否有問題。
12、判斷布局嵌套過多或者過于復雜
我們把Call+Recur Calls/Total和Cpu Time/Call放到最前面通過View/ViewGroup的draw調用次數和遞歸調用次數來判斷布局的層級過多或者布局Layout太多。也可以通過buildDisplayList函數的調用和遞歸調用次數來判斷布局的層級過多或者是Layout太多。
13、查類的初始化性能
我們把Call+Recur Calls/Total和Cpu Time/Call放到最前面通過Cpu Time/Call排序找到一些類的構造函數判斷類的初始化性能。類的初始化過程如果太久特別是在主線程中會影響性能而這個又是一個容易忽略的問題因為類的初始化過程可以簡單也可以復雜復雜的可以做懶加載來優化。如果調用次數多的那就更應該優化或者做復用。
14、排查字符串問題
把Call+Recur Calls/Total放到前面在搜索字符串相關的一些StringBuiler類或者StringBuffer類還有append方法以及enlarge方法來查看當前的字符串問題。找到調用方去掉不必要的字符串拼接和擴容來提升性能。
15、未開啟硬件加速
檢查繪制函數如果發現是drawSoftware那就是未開啟硬件加速影響了幀率。
16、排查集成的問題
有時候集成需要多個包可能會漏掉其中一個這一個時候通過TraceView調用分析自己的某個函數但是和自己的預期不一樣明明已經改過了為什么還會這樣這個時候可能就是打包的時候沒有引用到正確的包。
17、排查自己寫的函數是否符合預期
有時候自己寫的函數如字符串問題會被編譯器做一些優化或者不太注意用了很多+號導致了很多StringBuilder對象的分配這個時候通過TraceView我們可以發現在該函數下創建了多少個StringBuilder和以及擴容的問題。通過調用次數來判斷對性能的影響如果是頻率比較多的函數就應該去優化這些問題。
18、發現可復用對象
在對一些頻率較高的函數的子函數分析過程中我們可以去看是否每次這個函數調用的時候都會去創建這些對象如果是那可以考慮一下是否可以對這些對象做復用。如下圖發現這個重入鎖對象應該做復用。
19、判斷主線程長時間等待原因
時間線上主線程長時間空白可能是受其他因素的影響比如安全軟件對IO的監控用了鎖等待某個資源或者CPU太忙了沒有時間片來分配。下面第一張圖是因為安全軟件對IO的監控用了鎖等待某個資源導致主線程執行性能問題第二張圖則是由于其他現場太多太忙了導致主線程CPU分配不到時間片。
20、靈活運用時間線找出根源
在安卓代碼中我們不建議主動調用System.gc方法來觸發GC但是在檢測首頁滑動過程中LogCat中還是定時出現了GC_EXPLICIT的垃圾回收信息。通過啟用TraceView的跟蹤發現了調用System.gc的函數位置但是向上跟蹤后最終只能跟蹤到一個線程池的run具體這個線程的run由誰調用沒法繼續跟蹤了。這樣只能通過時間線上再去找問題通過鼠標放在時間線上從后到前簡單掃描了一下時間線并未發現和taobao,ali等包名的函數為了繼續排查只能放大時間線來發現線索通過放大時間線面板調用函數也會變得越來越細膩最終在調用gc前面部分位置找到了com.alibaba.mobileiim.channel.http.httpwebTokenCallback的函數調用從而定位到問題所在。
21、了解一些函數的性能問題如字符串函數格式化函數等
通過占用cpu百分比調用次數平均調用時間可以觀察到一些系統類實現的函數有性能問題在高頻率下不應該調用。
22、如果你對JAVA相當熟悉甚至可以通過這個軟件發現一些代碼上的問題
在分析一個高頻率函數的時候發現該函數包裝了一個subString方法但是子函數中卻多了一個String類的創建。待著問題查看了實現代碼發現該函數確實實現有問題。String是不可變對象source.substring函數本身就會返回指定的sub字符串內部會new一個string外部不需要再new string這樣多了一次對象的分配。
查看該文件的這個函數
public static String substring(String source, int start, int end) {if (end <= 0) {new String(source.substring(start));}new String(source.substring(start, end)); }23、一定不要忘了在各個界面的靜默狀態做跟蹤特別是有動畫的界面
在有廣告條輪播等動畫的界面尤其要注意像首頁、社區等界面都發現了在廣告條移出屏幕外的時候還有定時刷新廣告的問題這個會影響性能和耗電。有時候看代碼已經用Handler的removeCallbacks(this)接口移除了但隊列中可能還有其他的定時實例會重新啟動這個定時removeCallbacks只是在隊列中移除了這個實例相關的消息。替換成removeCallbacksAndMessages(null)函數移除全部等候執行的消息后才解決了該問題。
TraceView也是一個在界面切換到后臺被其他程序覆蓋等情況下檢查程序中仍再運行的線程等問題的首選工具。
以上是常用的TraceView性能跟蹤的一些方法,當然隨著使用的嫻熟你會發現它的功能并不止這些而且用的熟練后很容易就能找到影響性能的關鍵點。
彩蛋
?? 讀者朋友們,經過上面的介紹我想大家一定掌握了TraceView使用精髓了,是時候展現真正的技術實力的時候了,下面我提供一個TraceView的現場現場,看看讀者朋友們能否查出原因來。TraceView的文件見附件ddms_traceView.zip。
參考文章:
http://bxbxbai.github.io/2014/10/25/use-trace-view/
https://yq.aliyun.com/articles/20467
總結
以上是生活随笔為你收集整理的正确姿势使用TraceView工具的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 国内开源镜像(下载Linux系统)
- 下一篇: BP神经网络拟合函数