造成卡頓的原因有很多 最終會反映到CPU時間上
CPU時間分為兩種:
1??用戶時間
執(zhí)行用戶態(tài)應(yīng)用程序代碼消耗的時間
2??系統(tǒng)時間
執(zhí)行內(nèi)核態(tài)系統(tǒng)調(diào)用所消耗的時間 包括 I/O 鎖 中斷以及其他系統(tǒng)調(diào)用的時間
CPU性能
評價CPU的性能 需要看主頻 核心數(shù) 緩存等參數(shù)
獲取CPU信息:
// 獲取 CPU 核心數(shù)
cat /sys/devices/system/cpu/possible
// 獲取某個 CPU 的頻率
cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
卡頓問題分析指標
查看CPU的使用率 可以通過 /proc/stat 得到整個系統(tǒng)的CPU使用情況 通過 /proc/[pid]/stat得到某個進程的CPU使用情況
proc/self/stat:
utime: 用戶時間,反應(yīng)用戶代碼執(zhí)行的耗時
stime: 系統(tǒng)時間,反應(yīng)系統(tǒng)調(diào)用執(zhí)行的耗時
majorFaults:需要硬盤拷貝的缺頁次數(shù)
minorFaults:無需硬盤拷貝的缺頁次數(shù)
如果CPU使用率大于60% 表示系統(tǒng)處于繁忙狀態(tài) 需要進一步分析用戶時間和系統(tǒng)時間的比例
對于普通應(yīng)用程序 系統(tǒng)時間不會長期高于30% 如果超過 應(yīng)該進一步檢查是否I/O過多 還是其他系統(tǒng)調(diào)用問題
相關(guān)工具:
top 命令 查看哪個進程是CPU的消耗大戶
vmstat 命令 實時動態(tài)監(jiān)視操作系統(tǒng)的虛擬內(nèi)存和CPU
strace 命令 跟蹤某個進程中所有的系統(tǒng)調(diào)用
除了需要查看CPU的使用率還需要查看CPU飽和度(線程排隊等待CPU的情況 也就是CPU的負載情況)
CPU飽和度首先和應(yīng)用的線程數(shù)有關(guān) 如果啟動的線程過多 容易導(dǎo)致系統(tǒng)不斷切換執(zhí)行的線程 把大量的時間浪費在上下文切換 每一次CPU上下文切換都需要刷新寄存器和計數(shù)器 至少需要幾十納秒的時間
可以通過使用 vmstat 命令或者 /proc/[pid]/schedstat 文件來查看CPU上下文切換次數(shù) 特別需要注意 nr_involuntary_switches被動切換的次數(shù)
proc/self/sched:
nr_voluntary_switches:
主動上下文切換次數(shù),因為線程無法獲取所需資源導(dǎo)致上下文切換,最普遍的是 IO。
nr_involuntary_switches:
被動上下文切換次數(shù),線程被系統(tǒng)強制調(diào)度導(dǎo)致上下文切換,例如大量線程在搶占 CPU。
se.statistics.iowait_count:IO 等待的次數(shù)
se.statistics.iowait_sum: IO 等待的時間
通過uptime 命令可以檢查 CPU在1分鐘 5分鐘 和15分鐘內(nèi)的平均負載 比如一個4核的CPU 如果當(dāng)前平均負載是8 表明每個CPU又來了一個線程在運行 還有一個線程在等待
一般平均來負載應(yīng)該控制在 0.7*核數(shù) 以內(nèi)
另外一個影響CPU飽和度的是 線程優(yōu)先級 線程優(yōu)先級會影響Android系統(tǒng)的調(diào)度策略 主要由 nice 和 cgroup 類型共同決定 nice值越低 搶占CPU時間片的能力越強 當(dāng)CPU空閑時 線程的優(yōu)先級對執(zhí)行效率的影響不會特別明顯 但是在CPU繁忙時 影響就非常大
關(guān)于線程優(yōu)先級 需要注意是否存在高優(yōu)先級的線程空等低優(yōu)先級線程,例如主線程等待某個后臺線程的鎖
從應(yīng)用程序的角度看 用戶時間 系統(tǒng)時間 等待CPU的調(diào)度 都是程序運行花費的時間
Android卡頓排查工具
Traceview systrace 等
從實現(xiàn)上分兩個流派:
1?? instrument 獲取一段時間內(nèi)所欲嘔函數(shù)的調(diào)用過程 分析這個過程進一步分析優(yōu)化的點
2?? sample 有選擇性或者采用抽樣的方式觀察某些函數(shù)的調(diào)用過程 分析可疑點
Traceview
利用Android Runtime函數(shù)調(diào)用event事件 將函數(shù)運行的耗時和調(diào)用關(guān)系寫入trace文件中
屬于instrument可以查看整個過程有哪些函數(shù)調(diào)用 但是本身性能開銷大
Android 5.0 之后 新增了startMethodTracingSampling方法 使用基于樣本的方式進行分析 以減少對運行時的性能影響Nanoscope
instrument類型的性能分析工具 性能損耗較小
原理是 直接修改Android虛擬機源碼 在ArtMethod執(zhí)行入口和執(zhí)行結(jié)束位置增加賣點代碼 將所有的信息先寫到內(nèi)存 等到trace結(jié)束后才統(tǒng)一生成結(jié)果文件
限制:
1?? 需要自己刷ROM 并且當(dāng)前只支持Nexus 6P 或者對應(yīng)的模擬器
2?? 默認只支持主線程采集 其他線程需要代碼手動設(shè)置
非常適合做啟動耗時時的自動化分析
生成的是符合Chrome tracing規(guī)范的HTML文件 可以通過腳本來實現(xiàn)兩個功能:
1?? 反混淆 通過mapping自動反混淆結(jié)果文件
2?? 自動化分析 傳入相同的起點和終點 實現(xiàn)兩個結(jié)果文件的diff 自動分析差異systrace
可以跟蹤系統(tǒng)的I/O操作 CPU負載 Surface渲染 GC等
利用了Linux的ftrace調(diào)試工具 相當(dāng)于在系統(tǒng)各個位置添加了一些性能探針
Android 在ftrace的基礎(chǔ)上封裝了atrace增加了更多特有的探針 如Graphics、Activity Manager、Dalvik VM、System Server
只能監(jiān)聽特定系統(tǒng)調(diào)用的耗時情況 所以屬于sample類型 性能開銷很低 不支持應(yīng)用程序代碼的耗時分析
- Simpleperf
分析Native函數(shù)的調(diào)用 屬于sample類型 性能開銷很低
利用CPU的性能監(jiān)控單元(PMU)提供的硬件perf事件
使用Simpleperf可以看到所有的Native代碼的耗時 例如加載dex vertify class
Simpleperf同時封裝了systrace的監(jiān)控功能
發(fā)展的幾個階段:
第一個階段:在Android M和以前 Simpleperf不支持Java代碼分析
第二個階段:在Android O和以前 需要手動編譯OAT文件
第三個階段:在Android P以后 Simpleperf支持Java代碼分析
除了Nanoscope之外的工具都只支持debugable的應(yīng)用程序 需要root才能測試release包
總結(jié) 如果需要分析Native代碼的耗時 可以選擇Simpleperf;如果想分析系統(tǒng)調(diào)用 可以選擇systrace;如果想分析整個程序執(zhí)行流程的耗時 可以選擇Traceview或者插樁版本的systrace
可視化方法
Android Studio 3.2 的Profiler中集成了幾種性能分析工具:
-
Sample Java Methods的功能類似于Traceview的sample類型 -
Trace Java Methods類似Traceview的instrument類型 -
Trace System Calls類似systrace -
SampleNative(API Level 26+)類似Simpleperf
這些工具都支持Call Chart 和 Flame Chart 兩種展示方式
-
Call ChartTraceview 和 systrace 默認使用的展示方式 按照應(yīng)用程序的函數(shù)執(zhí)行順序來展示 適合分析整個流程的調(diào)用 -
Flame Chart火焰圖 以全局視野看待一段時間的調(diào)用分布s s s