捋一捋 App 性能測試中的幾個重要概念
作者 | 劉暢
杏仁測試工程師,專註移動端測試和自動化測試。
我們在使用各種 App 的時候基本會關注到:這款軟體挺耗流量的?運行起來設備掉電有點快嘛?切換頁面的時候還會有卡頓等現象?如果遇到有這些問題的 App 我們基本會將它請出我們的愛機。由此可見軟體是否受歡迎除了提供必要的功能外,流暢性、流量/電池消耗也是很重要的指標。
今天就來從我們測試人員的角度,談一談 App 驗收測試過程中需要關注到一些指標項目:
- 內存佔用
- CPU 佔用
- 流量耗用
- 電量耗用
- 啟動時間
因為最近就著騰訊 TMQ 團隊出版的《移動 App 性能評測與優化》這本書在看 App 性能測試這一塊的東西,看著看著發現有些名詞或者概念不是很明白,所以在看這塊東西的時候,還要一邊去查詢一些其他的點,現在將這些點記錄下來,也算是一篇讀書筆記了吧,下面針對每一個方面的一些重要知識點進行了整理。
一. 內存
1. 內存泄漏
說到內存方面,最經典的內存問題當數內存泄漏。百度上對內存泄漏的定義是這樣的:內存泄漏(Memory Leak)是指程序中己動態分配的堆內存由於某種原因程序未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統崩潰等嚴重後果。通俗點講,在大部分應用中,會有一類功能是需要載入附加資源的,比如顯示從網路下載的文本或圖片。這類功能往往需要在內存中存放要使用的資源對象,退出該功能後,就需要將這些資源對象清空。如果忘了清理,或者是代碼原因造成的清理無效,就會形成內存泄漏。
2. 垃圾回收
說到了內存泄漏,又不得不提到垃圾回收(Garbage Collector,簡稱 GC),內存中的垃圾,主要指的是內存中已無效但又無法自動釋放的空間,除非是重啟系統不然永遠也不會還給操作系統。這樣以來,時間久了當程序運行的時候就會產生很多垃圾,一方面浪費了不少內存空間,另一方面如果同一個內存地址被刪除兩次的話,程序就會不穩定,甚至奔潰。
在 Java 程序運行過程中,一個垃圾回收器會不定時地被喚起檢查是否有不再被使用的對象,並釋放它們佔用的內存空間。但垃圾回收器的回收是隨機的,可能在程序的運行的過程中,一次也沒有啟動,也可能啟動很多次,它並不會因為程序一產生垃圾,就馬上被喚起而自動回收垃圾。所以垃圾回收也並不能完全避免內存泄漏的問題。
另一方面,垃圾回收也會給系統資源帶來額外的負擔和時空開銷。它被啟動的幾率越小,帶來的負擔的幾率就越小。
3. 內存指標
內存指標有 VSS、RSS、PSS、USS,他們的含義分別是:
VSS:Virtual Set Size 虛擬耗用內存(包含共享庫佔用的內存)
RSS:Resident Set Size 實際使用物理內存(包含共享庫佔用的內存)
PSS:Proportional Set Size 實際使用的物理內存(按比例分配共享庫佔用的內存)
USS:Unique Set Size 進程獨自佔用的物理內存(不包含共享庫佔用的內存)
一般來說內存佔用大小有如下規律:VSS >= RSS >= PSS >= USS,一般測試中關注的比較多的是 PSS 這個指標。
4. 監控與分析工具
以下是幾種常見的內存分析工具,具體使用方法這裡就不詳述了。
4.1 Memory Monitor
該工具位於 Android Monitor 下面,Android Monitor 是 Android Studio 自帶的一個強大的性能分析工具,裡面一共包含 5 個模塊:Logcat、Memory、CPU、Network 及 GPU。
Memory Monitor 可以實時查看 App 的內存分配情況,判斷 App 是否由於 GC 操作造成卡頓以及判斷 App 的 Crash 是否是因為超出了內存。
4.2 Heap Viewer
該內存檢測工具位於 DDMS 下面,在 Android Studio 裡面可以通過 Tools-Android-Android Device Monitor 打開,Heap Viewer 可以實時查看 App 分配的內存大小和空閑內存大小,並且發現 Memory Leaks。
4.3 MAT
MAT(Memory Analyzer Tool),是一個被老生常談的 Android 內存分析工具,它可以清楚的獲知整體內存使用情況。雖然是 Eclipse 的工具,但也可以單獨運行,不需要安裝 Eclipse。
二. CPU
1. 時間片
時間片即 CPU 分配給各個程序的時間,每個線程被分配一個時間段,稱作它的時間片,即該進程允許運行的時間,使各個程序從表面上看是同時進行的。
2. Jiffies
2.1 Jiffies的概念
要講 Jiffies 需要先提到這兩個概念:HZ 和 Tick
HZ:Linux 核心每隔固定周期會發出 timer interrupt (IRQ 0),HZ 是用來定義每一秒有幾次 timer interrupts。例如 HZ 為 1000,就代表每秒有 1000 次 timer interrupts。
Tick:HZ 的倒數,Tick = 1/HZ,即 timer interrupt 每發生一次中斷的時間。如 HZ 為 250 時,tick 為 4 毫秒(millisecond)。
而 Jiffies 為 Linux 核心變數,是一個 unsigned long 類型的變數,被用來記錄系統自開機以來,已經過了多少 tick。每發生一次 timer interrupt,Jiffies 變數會被加 1。
2.2 查看 Jiffies 的方法
Linux 下使用命令cat /proc/stat
,查看具體整機的 Jiffies,如圖:
Linux 下使用命令cat /proc/<進程id>/stat
,查看具體某個進程的 Jiffies:
3. CPU 使用率
在 Linux 系統下,CPU 利用率分為用戶態、系統態和空閑態,他們分別代表的含義為:用戶態表示 CPU 處於用戶態執行的時間,系統態表示系統內核執行的時間,空閑態表示空閑系統進程執行的時間。
而一個 App 的 CPU 使用率 = CPU 執行非系統空閑進程時間 / CPU 總的執行時間,也可以表示為 App 用戶態 Jiffies + App 系統態 Jiffies / 手機總 Jiffies。
4. CPU 過高會帶來的影響
可能會使整個手機無法響應,整體性能降低,引起 ANR,導致手機更耗電,降低用戶體驗等。
三. 流量
1. 定義
我們的手機通過運營商的網路訪問 Internet,運營商替我們的手機轉發數據報文,數據報文的總大小(位元組數)即流量,數據報文是包含手機上下行的報文。
2. 常用流量測試方法
2.1 抓包測試法
主要是利用工具 Tcpdump 抓包,導出 pcap 文件,再在 wireshark 中打開進行分析。
2.2 統計測試法
2.2.1 讀取 linux 流量統計文件
利用 Android 自身提供的 TCP 收髮長度的統計功能,獲取 App 的 tcp_snd 和 tcp_rcv 的值,測試一段時間後再分別統計一次,用 tcp_snd
兩者的差值得到發送流量,用 tcp_rcv 兩者的差值得到接受流量。
2.2.2 利用 Android 流量統計 API
- TrafficStats
Android 2.2 版本開始加入 android.net.TrafficStats 類來實現對流量統計的操作。
部分方法如下:
static long getMobileRxBytes() //獲取通過移動數據網路收到的位元組總數 static long getMobileTxBytes() //通過移動數據網發送的總位元組數 static long getTotalRxBytes() //獲取設備總的接收位元組數 static long getTotalTxBytes() //獲取設備總的發送位元組數 static long getUidRxBytes(int uid) //獲取指定uid的接收位元組數 static long getUidTxBytes(int uid) //獲取指定uid的發送位元組數
- NetworkStatsManager
Android 6.0 版本開始,為了打破了原本 TrafficStats 類的查詢限制,官方又提供了 NetworkStatsManager 類,可以獲取更精準的網路歷史數據,也不再是設備重啟以來的數據。部分方法如下:
NetworkStats.Bucket querySummaryForDevice(int networkType, String subscriberId, long startTime, long endTime) // 查詢指定網路類型在某時間間隔內的總的流量統計信息NetworkStats queryDetailsForUid(int networkType, String subscriberId, long startTime, long endTime, int uid) // 查詢某uid在指定網路類型和時間間隔內的流量統計信息NetworkStats queryDetails(int networkType, String subscriberId, long startTime, long endTime) // 查詢指定網路類型在某時間間隔內的詳細的流量統計信息(包括每個uid)
四. 電量
1.耗電場景
- 定位,尤其是調用 GPS 定位。
- 網路傳輸,尤其是非 Wifi 環境。
- 屏幕亮度
- CPU 頻率
- 內存調度頻度
- wake_locker 時間和次數
- 其他感測器
2.測試方法
2.1 通過系統文件獲取電量記錄
使用命令 adb shell dumpsys batterystats> batterystats.txt
可以列印出詳細的耗電相關信息並保存統計的電量信息到 batterystats.txt 這個文件里。
2.2 通過導入 batterystats.txt 到 Google 開源工具 battery historian 進行分析
因為這個工具是 Go 語言開發,所以需要預裝Go語言開發環境,當然如果你不想配置Go語言環境,官方還提供了一種更方便的方案,通過安裝 docker 環境來使用這個工具。具體這個工具的配置安裝和具體使用方法以及參數的代表含義,我會單獨再寫一篇文章記錄,先拋磚引玉放一張這個工具的運行截圖。
3.優化方法
3.1 CPU 時間片
當應用退到後台運行時,盡量減少應用的主動運行,當檢測到 CPU 時間片消耗異常時,深入線程進行分析。
3.2 wake lock
前台運行時不要註冊 wake lock。
後台運行時,在保證業務需要的前提下,應盡量減少註冊 wake lock。
降低對系統的喚醒頻率, 使用 partial wake lock 代替 wake lock。
3.3 感測器
合理設置 GPS 的使用時長和使用頻率。
3.4 雲省電策略
考慮到用戶使用場景的多樣性,導致很難定位用戶異常耗電的根源,所以為了更深一層弄清楚這些問題,可以考慮定期上報灰度用戶手機電量數據的方式來分析問題。
五. 啟動時間
可使用命令 adb shell am start -W packagename/activity
查看 App 啟動耗時,查看了一下我們自己的 App Android 版本的啟動耗時如下:
注釋:
WaitTime:總的耗時,包括前一個應用 Activity pause 的時間和新應用啟動的時間
ThisTime:一連串啟動 Activity 的最後一個 Activity 的啟動耗時
TotalTime:新應用啟動的耗時,包括新進程的啟動和 Activity 的啟動,但不包括前一個應用 Activity pause 的耗時
六. 總結
App性能問題會直接影響產品體驗:耗流量、掉電快、卡頓、崩潰等現象會給用戶造成不良的體驗和印象,不利於產品的活躍及用戶留存。許多經驗豐富的程序員也會經常忽視這些不起眼的性能問題,因此作為測試人員,在版本發布前及早關注並發現上述性能問題就顯得尤其重要。但真正做起App性能測試才發現這條路並不容易走,抓取內存、CPU 等一些項目的指標容易,但對一些數據的敏感度和處理方式都是要靠經驗的慢慢積累。總之,性能測試算是一項比較繁瑣的工作,但難者不易,易者不難,希望已經行走在這條路上的或者準備踏上這條路的同行都能不斷提高自身素養,堅持到底。
參考
- Java中內存泄露及垃圾回收機制
- Android性能測試——CPU時間片
- 探秘App性能三角區
- Android性能專項測試之Batterystats
全文完
我們正在招聘 Java 工程師,歡迎有興趣的同學投遞簡歷到 rd-hr@xingren.com 。
歡迎搜索關注微信公眾號:杏仁技術站(微信號 xingren-tech)。
推薦閱讀:
※7 個使用 bcc/BPF 的性能分析神器
※像dotTrace這樣的profiler怎麼實現的?
※遺傳演算法框架GAFT優化小記
※同一代的賽揚,奔騰,酷睿和至強處理器性能差距到底有多大?
※Linux下做性能分析5:Amdahl模型