利用 Android Profiler 測量應用效能
本教程相當於官方教程的精簡版,將官方教程的乾貨提取出來。另外還寫了一個分析記憶體洩漏的例子。
Android Profiler能夠提供關於應用 CPU、記憶體和網路的實時資料。
啟動分析
要開啟 Android Profiler 視窗,請按以下步驟操作:
1. 點選工具欄中的 Android Profiler(也可以點選 View > Tool Windows > Android Profiler )。
2. 在 Android Profiler 視窗頂部選擇想要分析的裝置和應用程序,如下圖所示。
上圖中各個數字對應的含義:
① 要分析的裝置。
② 要分析的應用程序。
③ 時間線縮放控制元件。
④ 實時更新跳轉按鈕。
⑤ Event時間線,包括Activity狀態、使用者輸入Event和螢幕旋轉Event。
如果顯示“Advanced profiling is unavailable for the selected process”,可以在頂層工具欄中點選 Profile 'app' 來執行,或者在執行配置中啟用高階分析,按以下步驟操作:
1. 選擇 Run > Edit Configurations。
2. 在左側窗格中選擇您的應用模組。
3. 點選 Profiling 標籤,然後勾選 Enable advanced profiling
重新構建並執行應用即可。
CPU Profiler
CPU Profiler 可幫助實時檢查應用的 CPU 使用率和執行緒 Activity,並記錄函式跟蹤,以便優化和除錯應用程式碼。
CPU Profiler 概覽
如上圖所示,CPU Profiler 的預設檢視包括以下內容:
① Event 時間線: 顯示應用中在其生命週期轉換的 Activity,並顯示使用者與裝置的互動,包括螢幕旋轉 Event。
② CPU 時間線: 顯示應用的實時 CPU 使用率(佔總可用 CPU 時間的百分比)以及應用使用的匯流排程數。 此時間線還顯示其他程序的 CPU 使用率(如系統程序或其他應用),以便可以將其與自己的應用使用率進行對比。 通過沿時間線的水平軸移動滑鼠,還可以檢查歷史 CPU 使用率資料。
③ 執行緒 Activity 時間線: 列出屬於應用程序的每個執行緒。下面說明不同的顏色對應的含義:
- 綠色: 表示執行緒處於活動狀態或準備使用 CPU。 即,它正在“執行中”或處於“可執行”狀態。
- 黃色: 表示執行緒處於活動狀態,但它正在等待一個 I/O 操作(如磁碟或網路 I/O),然後才能完成它的工作。
- 灰色: 表示執行緒正在休眠且沒有消耗任何 CPU 時間。 當執行緒需要訪問尚不可用的資源時偶爾會發生這種情況。 執行緒進入自主休眠或核心將此執行緒置於休眠狀態,直到所需的資源可用。
④ 記錄配置: 選擇分析器記錄函式跟蹤的方式,如下:
- Sampled: 以固定週期記錄。在應用執行期間頻繁捕獲應用的呼叫堆疊。 分析器比較捕獲的資料集以推導與應用程式碼執行有關的時間和資源使用資訊。 基於“Sampled”的跟蹤的問題是,如果應用在捕獲呼叫堆疊後進入一個函式並在下一次捕獲前退出該函式,則分析器不會記錄該函式呼叫。 如果對此類生命週期很短的跟蹤函式感興趣,應使用“Instrumented”跟蹤。
- Instrumented: 以函式呼叫時間為週期記錄。在執行時設定應用以在每個函式呼叫的開始和結束時記錄時間戳。 它收集時間戳並進行比較,以生成函式跟蹤資料,包括時間資訊和 CPU 使用率。 注意,與設定每個函式關聯的開銷會影響執行時效能,並可能會影響分析資料,對於生命週期相對較短的函式,這一點更為明顯。 此外,如果應用短時間內執行大量函式,則分析器可能會迅速超出它的檔案大小限制,且不能再記錄更多的跟蹤資料。
- Edit configurations: 允許更改上述“Sampled”和“Instrumented”記錄配置的某些預設值,並將它們另存為自定義配置。
⑤ 記錄按鈕: 用於開始和停止記錄函式跟蹤。
注: 分析器還會報告 Android Studio 和 Android 平臺新增到您的應用程序(如 JDWP、Profile Saver、Studio:VMStats、Studio:Perfa 以及 Studio:Heartbeat)的執行緒 CPU 使用率。
記錄和檢查函式跟蹤
選擇 Sampled 或 Instrumented ,然後點選Record開始記錄函式跟蹤,點選Stop recording結束,如下圖所示。
① 選擇時間範圍: 確定要在跟蹤窗格中檢查所記錄時間範圍的哪一部分。 當首次記錄函式跟蹤時,CPU Profiler 將在 CPU 時間線中自動選擇完整長度。 如果想僅檢查所記錄時間範圍一小部分的函式跟蹤資料,可以點選並拖動突出顯示的區域邊緣以修改其長度。
② 時間戳: 用於表示所記錄函式跟蹤的開始和結束時間(相對於分析器從裝置開始收集 CPU 使用率資訊的時間)。 可以點選時間戳以自動選擇完整記錄。
③ 跟蹤窗格: 用於顯示所選的時間範圍和執行緒的函式跟蹤資料。
④ 通過呼叫圖表、火焰圖、 Top Down 樹或Bottom Up 樹的形式顯示函式跟蹤。
⑤ 確定如何測量每個函式呼叫的時間資訊:
- Wall clock time:實際經過的時間。
- Thread time:實際經過的時間減去執行緒沒有消耗 CPU 資源的時間。
使用 Call Chart 標籤檢查跟蹤
Call Chart 標籤提供函式跟蹤的圖形表示形式,其中,水平軸表示函式呼叫(或呼叫方)的時間,並沿垂直軸顯示其被呼叫者。 對系統 API 的函式呼叫顯示為橙色,對應用自有函式的呼叫顯示為綠色,對第三方 API(包括 Java 語言 API)的函式呼叫顯示為藍色。 下圖展示了一個呼叫圖表示例,並描繪了給定函式的 Self time、Children time 以及總時間的概念。
提示: 若要跳轉到某個函式的原始碼,請右鍵點選該函式並選擇 Jump to Source。
使用 Flame Chart 標籤檢查跟蹤
Flame Chart 標籤提供一個倒置的呼叫圖表,其中水平軸不再代表時間線,它表示每個函式相對的執行時間。
下面說明此概念,考慮下圖中的呼叫圖表。注意函式 D 多次呼叫 B(B1、B2 和 B3),其中一些對 B 的呼叫也呼叫了 C(C1 和 C3)。
由於 B1、B2 和 B3 共享相同的呼叫方順序 (A → D → B),因此可將它們彙總在一起,如下所示。 同樣,將 C1 和 C3 彙總在一起,因為它們也共享相同的呼叫方順序 (A → D → B → C)。注意未包含 C2,因為它具有不同的呼叫方順序 (A → D → C)。
彙總的函式呼叫用於建立火焰圖,如下圖所示。注意,對於火焰圖中任何給定的函式呼叫,首先顯示消耗最多 CPU 時間的被呼叫方。
使用 Top Down 和 Bottom Up 檢查跟蹤
Top Down 標籤顯示一個函式呼叫列表,在該列表中展開函式節點會顯示函式的被呼叫方。 下圖顯示上面呼叫圖表對應的“Top Down”圖表。圖表中的每個箭頭都從呼叫方指向被呼叫方。
如上圖所示,在“Top Down”標籤中展開函式 A 的節點可顯示它的被呼叫方,即函式 B 和 D。 然後,展開函式 D 的節點可顯示它的被呼叫方,即函式 B 和 C 等等。
Top Down 標籤提供以下資訊以說明在每個函式呼叫上所花費的 CPU 時間:
- Self: 表示函式呼叫在執行自己的程式碼(而非被呼叫方的程式碼)上所花的時間。
- Children: 表示函式呼叫在執行自己的被呼叫方(而非自己的程式碼)上所花的時間。
- 總和: 函式的 Self 和 Children 時間的總和。 表示應用在執行函式呼叫上所花的總時間。
Bottom Up 標籤顯示一個函式呼叫列表,在該列表中展開函式節點將顯示函式的呼叫方,如下圖所示。
如上圖所示,在“Bottom Up”樹中開啟函式 C 的節點可顯示它的呼叫方 B 和 D。 注意,儘管 B 呼叫 C 兩次,但在“Bottom Up”樹中展開函式 C 的節點時,B 僅顯示一次。 然後,展開 B 的節點顯示其呼叫方,即函式 A 和 D。
注:當分析器到達檔案大小限制時,Android Studio 將停止收集新資料(但不會停止記錄)。
建立記錄配置
要建立或編輯自定義配置,或檢查現有的預設配置,可通過記錄配置下拉選單中選擇 Edit configurations 來開啟 CPU Recording Configurations 對話方塊,如下圖所示。
可以在左側窗格中選擇現有配置來檢查其設定,也可按如下方式建立一個新的記錄配置:
1. 點選對話方塊左上角的 ➕ 。
2. 為配置命名。
3. 在 Trace Technology 部分選擇 Sampled 或 Instrumented。
4. 對於“Sampled”記錄配置,以微秒 (μs) 為單位指定 Sampling interval。 此值表示應用呼叫堆疊的每個抽樣之間的持續時間。
5. 對於寫入連線裝置的記錄資料,以兆位元組 (MB) 為單位指定 File size limit。 當您停止記錄時,Android Studio 將解析此資料並將其顯示在分析器視窗中。
注: 如果使用執行 API 級別 26 或更高版本的連線裝置,則對於跟蹤資料的檔案大小沒有限制,此值可忽略。
6. 點選 Apply 或 OK。 如果更改了其他記錄配置,則也將儲存這些更改。
Memory Profiler
Memory Profiler 是可幫助識別導致應用卡頓、凍結甚至崩潰的記憶體洩漏和流失。 它顯示一個應用記憶體使用量的實時圖表,可以捕獲堆轉儲、強制執行垃圾回收以及跟蹤記憶體分配。
Memory Profiler 概覽
如上圖所示,Memory Profiler 的預設檢視包括以下各項:
① 強制執行垃圾回收 Event 。
② 捕獲堆轉儲。
③ 記錄記憶體分配情況。 此按鈕僅在執行 Android 7.1 或更低版本的裝置時才會顯示。
④ 放大/縮小時間線。
⑤ 跳轉至實時記憶體資料。
⑥ Event 時間線,其顯示 Activity 狀態、使用者輸入 Event 和螢幕旋轉 Event。
⑦ 記憶體使用量時間線,包含以下內容:
- 顯示每個記憶體類別使用多少記憶體的堆疊圖表,如左側的 y 軸以及頂部的彩色鍵所示。
- 虛線表示分配的物件數,如右側的 y 軸所示。
- 用於表示每個垃圾回收 Event 的圖示。
如上圖,記憶體計數中的類別如下所示:
- Java:從 Java 或 Kotlin 程式碼分配的物件記憶體。
- Native:從 C 或 C++ 程式碼分配的物件記憶體。
- Graphics:圖形緩衝區佇列向螢幕顯示畫素(包括 GL 表面、GL 紋理等等)所使用的記憶體。 (注意,這是與 CPU 共享的記憶體,不是 GPU 專用記憶體。)
- Stack: 應用中的原生堆疊和 Java 堆疊使用的記憶體,通常與應用執行多少執行緒有關。
- Code:應用用於處理程式碼和資源(如 dex 位元組碼、已優化或已編譯的 dex 碼、.so 庫和字型)的記憶體。
- Other:應用使用的系統不確定如何分類的記憶體。
- Allocated:應用分配的 Java/Kotlin 物件數。 沒有計入 C 或 C++ 中分配的物件。
注:目前,Memory Profiler 還會顯示應用中的一些誤報的原生記憶體使用量,而這些記憶體實際上是分析工具使用的。 對於大約 100000 個物件,最多會使報告的記憶體使用量增加 10MB。
檢視記憶體分配
Memory Profiler 可顯示有關物件分配的以下資訊:
- 分配哪些型別的物件以及它們使用多少空間。
- 每個分配的堆疊追蹤,包括在哪個執行緒中。
- 物件在何時被取消分配(Android 8.0+)。
如果裝置執行 Android 8.0 或更高版本,可以按照下述方法檢視物件分配: 只需點選並按住時間線,並拖動選擇想要檢視分配的區域,如下圖所示:
如果裝置執行 Android 7.1 或更低版本,則在 Memory Profiler 工具欄中點選 Record memory allocations 。 操作完成後,點選 Stop recording 以檢視分配。如下圖所示:
在選擇一個時間線區域後,已分配物件的列表將顯示在時間線下方,按類名稱進行分組,並按其堆計數排序。
注:在 Android 7.1 及更低版本上,最多可以記錄 65535 個分配。 如果記錄超出此限值,則記錄中僅儲存最新的 65535 個分配。 在 Android 8.0 及更高版本中,沒有此限制。
要檢查分配記錄,請按以下步驟操作:
1. 瀏覽列表以查詢堆計數異常大且可能存在洩漏的物件。 點選 Class Name 列標題可以按字母順序排序。 然後點選一個類名稱。 此時在右側將出現 Instance View 窗格,顯示該類的每個例項。
2. 在 Instance View 窗格中,點選一個例項。 此時下方將出現 Call Stack 標籤,顯示該例項被分配到何處以及在哪個執行緒中,如下圖所示。
3. 在 Call Stack 標籤中,雙擊任意行以在編輯器中跳轉到該程式碼。
預設情況下,左側的分配列表按類名稱排列。 在列表頂部,可以使用右側的下拉列表在以下排列方式之間進行切換:
- Arrange by class:基於類名稱對所有分配進行分組。
- Arrange by package:基於軟體包名稱對所有分配進行分組。
- Arrange by callstack:將所有分配分組到其對應的呼叫堆疊。
捕獲堆轉儲
堆轉儲顯示在捕獲堆轉儲時應用中哪些物件正在使用記憶體。 特別是在長時間的使用者會話後,堆轉儲會顯示您認為不應再位於記憶體中卻仍在記憶體中的物件,從而幫助識別記憶體洩漏。 在捕獲堆轉儲後,可以檢視以下資訊:
- 應用已分配哪些型別的物件,以及每個型別分配多少。
- 每個物件正在使用多少記憶體。
- 在程式碼中的何處仍在引用每個物件。
- 物件所分配到的呼叫堆疊。 (目前,如果在記錄分配時捕獲堆轉儲,則只有在 Android 7.1 及更低版本中,堆轉儲才能使用呼叫堆疊。)
要捕獲堆轉儲,在 Memory Profiler 工具欄中點選 Dump Java heap 。 在轉儲堆期間,Java 記憶體量可能會暫時增加, 這很正常,因為堆轉儲與您的應用發生在同一程序中,並需要一些記憶體來收集資料。
堆轉儲顯示在記憶體時間線下,顯示堆中的所有類型別,如下圖所示。
要檢查堆,請按以下步驟操作:
1. 瀏覽列表以查詢堆計數異常大且可能存在洩漏的物件。 點選 Class Name 列標題可以按字母順序排序。 然後點選一個類名稱。 此時在右側將出現 Instance View 窗格,顯示該類的每個例項。
2. 在 Instance View 窗格中,點選一個例項,此時下方將出現 References,顯示該物件的每個引用。點選例項名稱旁的箭頭可以檢視其所有欄位,然後點選一個欄位名稱檢視其所有引用。 如果要檢視某個欄位的例項詳情,右鍵點選該欄位並選擇 Go to Instance,如下圖所示。
3. 在 References 標籤中,如果發現某個引用可能在洩漏記憶體,則右鍵點選它並選擇 Go to Instance。
在類列表中,可以檢視以下資訊:
- Heap Count:堆中的例項數。
- Shallow Size:此堆中所有例項的總大小(以位元組為單位)。
- Retained Size:為此類的所有例項而保留的記憶體總大小(以位元組為單位)。
在類列表頂部,可以使用左側下拉列表在以下堆轉儲之間進行切換:
- Default heap:系統未指定堆時。
- App heap:應用在其中分配記憶體的主堆。
- Image heap:系統啟動映像,包含啟動期間預載入的類。 此處的分配保證絕不會移動或消失。
- Zygote heap:寫時複製堆,其中的應用程序是從 Android 系統中派生的。
預設情況下,此列表按 Retained Size 列排序,可以點選任意列標題以更改列表的排序方式。
在 Instance View 中,每個例項都包含以下資訊:
- Depth:從任意 GC root 到所選例項的最短 hop 數。
- Shallow Size:此例項的大小。
- Retained Size:此例項支配的記憶體大小。
將堆轉儲另存為 HPROF
如果要儲存堆轉儲以供日後檢視,可通過點選時間線下方工具欄中的 Export heap dump as HPROF file,將堆轉儲匯出到一個 HPROF 檔案中。 在顯示的對話方塊中,確保使用 .hprof 字尾儲存檔案。然後,通過將此檔案拖到一個空的編輯器視窗就可以在 Android Studio 中開啟該檔案。
要使用其他 HPROF 分析器(如 jhat),需要將 HPROF 檔案從 Android 格式轉換為 Java SE HPROF 格式。 可以使用 android_sdk/platform-tools/ 目錄中的 hprof-conv 工具執行此操作。 執行該命令需要兩個引數:原始 HPROF 檔案和轉換後 HPROF 檔案的寫入位置。 例如:
hprof-conv heap-original.hprof heap-converted.hprof
分析記憶體洩漏示例
下面舉例說明如何分析記憶體洩漏。
下圖點選中間可以進入一個產生記憶體洩漏的Activity,下面展示如何通過Memory Profiler找到產生記憶體洩漏的類並定位到產生洩漏的程式碼。
首先,我們可以反覆進入和退出記憶體洩漏Activity,然後在Memory Profiler中強制垃圾回收,之後捕獲堆轉儲。待分析結果出來後,可以點選右側的篩選按鈕,篩選我們關注的包名,如下圖所示:
也可以在“Arrange by class”處選擇“Arrange by package”來進行手動選擇。
可以看到有3個MemoryLeakActivity物件,按理說退出它們並GC之後不應該在記憶體中。我們單擊那一行,右側會顯示每個例項,點選其中一個例項,下面會顯示其引用,如下圖所示:
我們可以雙擊第一個引用,或者右鍵點選選擇“Jump to Source”,就可以定位到產生記憶體洩漏的程式碼,如下:
這樣一個記憶體洩漏的分析過程就結束了。
Network Profiler
Network Profiler 能夠在時間線上顯示實時網路 Activity,包括髮送和接收的資料以及當前的連線數,便於檢視應用傳輸資料的方式和時間,並據此對底層程式碼進行適當優化。
Network Profiler 概覽
如上圖所示,視窗頂部顯示的是 Event 時間線以及無線裝置功耗狀態(低/高)與 WLAN 的對比①。 在時間線上,可以點選並拖動選擇時間線的一部分來檢查網路流量②。 下方的視窗③會顯示在時間線的選定片段內收發的檔案,包括檔名稱、大小、型別、狀態和時間。 可以點選任意列標題排序。 同時,還可以檢視時間線選定片段的明細資料,顯示每個檔案的傳送和接收時間。
點選網路連線的名稱即可檢視有關所傳送或接收的選定檔案的詳細資訊④。 點選各個標籤可檢視響應資料、標題資訊和呼叫堆疊。
注:Network Profiler 目前只支援 HttpURLConnection 和 OkHttp 網路連線庫。
參考:
https://developer.android.com/studio/profile/android-profiler
https://blog.csdn.net/niubitianping/article/details/72617864