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