Android Studio 4.1 中的本地內存分析

本文是 Android Studio 4.1 中 Profiler 有哪些新增特性 的第二部分。以前的文章側重於介紹 Android Studio 中 System Trace 的新增功能android

咱們從你們的反饋瞭解到使用 C++ 調試本地內存很是困難,尤爲在開發遊戲的時候。在 Android Studio 4.1 中,Memory Profiler (內存分析器) 能夠記錄本地內存分配的調用棧。本地內存記錄基於 Perfetto 後端實現,它是 Android 的新一代性能工具和問題追溯解決方案。git

在調試內存問題的時候,一般的作法是搞清楚什麼在佔用內存,什麼在釋放內存。本文接下來會帶着你們一塊兒使用 Native Memory Profiler 來發現內存泄漏,而且使用 GPU 模擬壓力測試 (Gpu Emulation Stress Test) 做爲示例工程。github

準備工做

首先,咱們從 https://github.com/google/gpu-emulation-stress-test 克隆或者下載源碼。正則表達式

當咱們發現可疑的內存泄漏時,最好的作法是從更高的層次開始而且觀察系統內存的圖形。您只須要在 Android Studio 中點擊 profile 按鈕,而後打開內存分析器,裏面會顯示更加詳細的內存追蹤信息。後端

內存分析器的頂層視圖,從顯示中能夠看到每次運行 "GPU emulation stress test" 的時候內存佔用都會逐步升高

內存分析器的頂層視圖,從顯示中能夠看到每次運行 "GPU emulation stress test" 的時候內存佔用都會逐步升高

運行了幾回模擬器後,咱們能夠發現一些有趣的現象:app

  1. 對於 GPU 模擬應用來講,GPU 內存增長是理所應當的,然而 Activity 被 finish 以後,該內存彷佛被清空了。
  2. 每當咱們打開 GPUEmulationStressTestActivity 的時候,本地內存都會有所增長,可是每次運行後該內存彷佛並無被重置,這就代表可能存在內存泄漏。

Native Memory Table (本地內存表格) 視圖

從 Android Studio 4.1 Canary 6 開始,咱們能夠經過獲取本地內存分配記錄來分析爲什麼內存未被釋放。爲了可以在 GPU 模擬應用上進行該項操做,我先中止正在運行的應用,而後啓動分析一個新的實例。從一個初始的狀態開始,會有助於咱們縮小須要關注的範圍,尤爲是在研究一套不熟悉的代碼的時候更是如此。經過內存分析器,我能夠得到整個 GPU 模擬示例運行期間的本地內存分配記錄。咱們須要點擊 Run->Profile-> ‘app’ 來重啓應用。應用啓動後 profile 窗口會打開,點擊內存分析器,而後選擇 record native allocationide

本地內存記錄在 Android Studio 中加載時的初始狀態

本地內存記錄在 Android Studio 中加載時的初始狀態

有些遊戲或者應用所依賴的庫會在 new 關鍵字以外調用 malloc 來申請內存。這個表格視圖突出顯示了這種狀況,於是在應對這類遊戲或應用時很是有用。函數

當記錄加載後,數據會以表格的形式呈現。表格中會顯示調用 malloc 的葉子函數。除了顯示函數名,表格裏還會包含模塊、調用計數、空間大小、和 delta 值。這些信息會被進行採樣,所以不是全部的 malloc 或 free 的調用都會被捕捉到。這很大程度上取決於採樣率,後面咱們會討論它。工具

另外頗有必要了解這些佔用內存的函數是被哪些函數調用的。有兩種方法可圖形化該信息。第一種方法是將 "Arrange by allocation method" 選項改成 "Arrange by call stack"。表格會顯示調用棧的樹結構,和 CPU 記錄裏的相似。若是當前項目包含符號 (一般適用於可調試構建,若是您正在分析一個外部的 APK,能夠參考一下 文檔),他們會自動被選取並啓用。這樣您就能夠經過右鍵點擊函數並 "Jump to source" 來直接轉向源碼。性能

在表格裏右鍵點擊一個元素會顯示 "Jump to Source" 菜單

在表格裏右鍵點擊一個元素會顯示 "Jump to Source" 菜單

內存可視化 (本地和非本地)

咱們還在內存分析器中增長了用於可視化數據的火焰圖,您能夠很是快速地找到分配內存最多的調用棧。該方法對於很深的調用棧很是有用。

有四種方式能夠在 X 軸上對數據進行排序:

  • "分配容量" (Allocation Size) 屬於默認值,表示被追蹤的內存總量;
  • "分配計數" (Allocation Count) 表示分配內存的對象總數;
  • "所有剩餘容量" (Total Remaining Size) 表示在數據採集結束以前,整個數據採集過程當中未被釋放的內存容量;
  • "所有剩餘計數" (Total Remaining Count) 和剩餘容量相似,表示在採集結束以前,整個採集過程當中未被釋放的對象總數。

採集數據加載以後,在 "所有剩餘容量" 視圖裏,很容易發現 "lodepng" 所分配的內存容量比較大

採集數據加載以後,在 "所有剩餘容量" 視圖裏,很容易發現 "lodepng" 所分配的內存容量比較大

從這裏咱們能夠直接右鍵點擊調用棧,而後選擇 "轉向源碼" (Jump to Source),而後會直接轉向內存分配相關的源碼。不過,咱們稍微花些時間看一下這裏的可視化圖形,會發現這裏共享的父節點 WorldState 形成了多個泄漏問題。要驗證這點,能夠經過圖形來過濾結果。

過濾/導航

和表格視圖相似,圖表能夠經過過濾欄 (filter bar) 進行數據過濾。當啓用過濾的時候,圖表的數據會自動進行更新,僅顯示函數符合關鍵詞或者正則表達式的調用棧。

有的時候調用棧會比較長,或者僅僅由於屏幕的空間不足而沒法完整顯示所有函數的名稱。您可使用 ctrl 加鼠標滾輪進行縮放,或者能夠點擊圖表,使用 W、A、S、D 進行導航。

驗證結果

增長斷點,而且快速運行兩次模擬器,而後發現第二次運行的時候,因爲咱們覆蓋了第一次運行時的一個指針形成了內存泄漏。

調試器的 Quick 視圖顯示第二次運行的時候 "sWorld" 已經有值了

調試器的 Quick 視圖顯示第二次運行的時候 "sWorld" 已經有值了

做爲快速解決方案,咱們能夠在處理結束後釋放掉 sWorld 變量,而後再次分析應用來驗證問題是否解決。

咱們仍是觀察高層次的內存統計。驗證了在模擬運行結束的時候刪除 sWorld 釋放了最初運行時佔用的 70 MB。

應用啓動分析和採樣率設置

上面的例子展現瞭如何經過本地內存追蹤來定位和解決內存泄漏問題。另外一個本地內存追蹤的常見用法是理解應用啓動時內存的佔用狀況。在 Android Studio 4.1 中,咱們還增長了在應用啓動時採集本地內存使用記錄的功能。您能夠在 "Run/Debug Configuration" 裏的 "Profiling" 標籤頁進行設置。

Profiling 標籤頁位於 Run Configuration 對話框中

Profiling 標籤頁位於 Run Configuration 對話框中

您能夠在 Run 配置對話框中自定義採集間隔或者設置應用啓動時記錄內存使用狀況。

這裏您還能夠爲新的採集修改採樣率。更小的採樣率會對整個性能產生很大的影響,而更大的採樣率則會遺漏一些內存分配記錄。不一樣的採樣率針對不一樣類型的內存問題。

總結一下

經過全新的本地內存分析器能夠定位內存泄漏而且輕鬆洞悉內存使用狀況。快去 Android Studio 4.1 試試本地內存分析器吧。若是有任何問題和反饋能夠 給咱們留言。更多小竅門能夠查閱咱們今年早些時候在 Google 遊戲峯會分享的內容:

https://www.bilibili.com/vide...

相關文章
相關標籤/搜索