鴻蒙應用開發過程中,可能由于種種原因導致應用內存未被正的使用或者歸還至操作系統,從而引發內存異常占用、內存泄漏等問題,最終導致應用卡頓甚至崩潰,嚴重影響用戶體驗。
DevEco Profiler是集成在DevEco Studio中的一款原生鴻蒙應用性能優化工具,能夠輔助開發者高效完成鴻蒙應用的性能問題定位與優化。在集成開發環境DevEcoStudio中可以以如下方式打開DevEco Profiler:
在DevEco Studio頂部菜單欄中選擇“View-> Tool Windows -> Profiler”。
在DevEco Studio底部工具欄中單擊“Profiler”。
按“Double Shift”或者“Ctrl+Shift+A”打開搜索功能,搜索“Profiler”。
DevEco Profiler中提供了針對鴻蒙應用內存問題的場景化分析模板SnapshotInsight與Allocation Insight,可以用于分析ArkTS以及Native內存,幫助開發者高效定位解決內存問題。下面將從識別問題、定界定位、優化驗證三個方面來對DevEcoProfiler定位內存問題的方法進行介紹。
識別內存問題
1.1 監控應用內存
當應用的某項功能開發完成時,可以使用DevEco Profiler的實時監控功能對應用的各項資源進行監控,其中就包括應用內存資源。詳細使用方法見性能問題定界。
性能問題定界:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/realtime-monitor-V13?catalogVersion=V13
實時監控界面所展示的是應用在運行過程中實際所使用的物理內存(ProportionalSet Size, PSS)、其他進程的物理內存占用以及操作系統的空閑內存,泳道的藍色部分展示了當前進程的物理內存占用情況及趨勢,左側餅圖則展示了當前時刻的瞬時內存使用數據。
此時,我們可以正常地操作應用,觀察在當前功能運行過程中的應用內存變化情況,初步判斷是否存在內存問題。當發現在一段時間內應用內存沒有明顯增加或者在內存上漲后又逐漸回落至正常水平,則基本可以排除應用存在內存問題;反之,如果應用內存占用明顯與預期值不符合、在一段時間內不斷上漲且無回落或者內存占用明顯增長超出預期,那么則可初步判斷應用可能存在內存問題,需要進一步分析。
1.2 初步定界內存問題
當從實時監控頁面初步判斷應用可能存在內存問題的時候,進一步地可以使用AllocationInsight或Snapshot Insight的Memory泳道來抓取應用內存在問題場景下的詳細數據以及變化趨勢,初步定界問題出現的位置(NativeHeap/ArkTS Heap/dev段等)。
在當前步驟下,錄制內存數據時需要將Allocation Insight或SnapshotInsight中的其余泳道去除勾選,僅錄制Memory泳道的數據(注:因為其余泳道會開啟對內存分配、內存對象等數據的抓取,這些功能會帶來額外的開銷,可能會對我們初步定界問題產生噪音,影響分析,故先排除錄制)。
在Memory泳道的錄制過程中,不斷操作應用在問題場景的功能,將問題放大,便于快速定界問題點,參考錄制過程中的應用內存占用曲線,當曲線的上漲幅度達到一定大小時即可結束錄制。
錄制完成后,可以展開Memory泳道,查看應用內存分段的使用情況,也可以點擊泳道上的options下拉框,自行選擇想要關注變化情況的內存類型。
同時,可以選中Memory泳道或其任一子泳道(直接選中泳道時詳情區域會展示完整的泳道數據)來查看在每個采樣點的詳細應用內存占用數據(注:詳情區域數據采用PSS的維度衡量,數據近似于使用`hidumper --mem $pid`的第一列PSS值)。當鼠標左鍵單擊選中表格中某一行的數據時,對應的泳道上將展示出當前的時間刻度線,方便快速定位內存變化的時間位置。
通過查看Memory的子泳道內存分類以及詳情區域的內存詳細占用,我們能夠大致定界出有哪些位置的內存可能存在問題。例如上圖中從Memory的子泳道數據圖中可以看到,FilePageOther/Native Heap/ArkTS Heap均有較大的增長,因而可以以這三個方面的內存使用作為切入點,來進一步分析問題的根因。
定位內存問題
從1.2節中的分析可知,當前應用內存的增長主要集中在FilePage Other/NativeHeap/ArkTS Heap這三個部分,那么需要使用進一步的分析方法對這三個方面的內存進行分析,定位內存上漲的根因。
在分析鴻蒙應用的內存問題時,可以將鴻蒙應用的內存大體分為兩部分,方舟虛擬機內存和Native內存:
1. 方舟虛擬機內存:由方舟虛擬機管控的應用內存,同其他的虛擬機內存(例如Java)管理策略相似,開發者可以使用并操控的內存基本集中于虛擬機堆上,在方舟虛擬機上被稱作ArkTSHeap,這部分內存受到方舟虛擬機的管控。
2. Native內存:這部分內存主要是應用使用到的一些涉及Native的API所申請的內存以及開發者自己的Native代碼所申請使用的堆內存(通常是C/C++),這部分內存需要開發者自己去管理申請和釋放。
因為兩種內存的使用方式和管理方式不盡相同,因此在對這兩類內存的分析過程中所使用的方法也有比較大的區別,下面將從這兩個方面分別介紹分析方法。
2.1 ArkTS內存問題
2.1.1 ArkTS內存管理
首先針對ArkTS Heap,由于該部分堆內存受到方舟虛擬機的管理,可以對堆內存進行垃圾回收(GarbageCollect,GC)和拍攝快照(HeapSnapshot或HeapDump,簡稱dump)以反映出瞬時的全量堆內存使用及分布情況,因此這部分內存的問題分析手段主要是對堆dump進行引用關系分析,分析泄漏對象無法被GC回收的原因。
不同于引用計數算法,方舟虛擬機采用可達性分析的機制來管理對象是否可被垃圾收集器回收,因此針對方舟虛擬機內存的分析方法主要集中于對象的引用分析,即分析哪些對象引用關系是錯誤的或者異常的,從而導致了泄漏對象被長時間持有無法被GC,最終通過解除強引用關系的手段來解決內存泄露問題。如下圖1所示,藍色的對象節點表示在內存引用分析中該對象GCRoot引用可達,其余對象GC Root引用不可達,引用不可達的對象在GC中可以被虛擬機回收。
圖1 對象可達性分析
2.1.2 ArkTSHeap分析
針對虛擬機的內存問題分析通常都集中在對dump的對象分布及引用關系的分析上,方舟虛擬機也不例外,這里在DevEcoProfiler上提供了Snapshot Insight來對方舟虛擬機的堆內存進行分析。
在使用Snapshot分析時,通常會使用三快照技術(Three Snapshot Technique),通過內存快照的對比視圖將某兩次快照之間分配且仍然駐留的內存篩選出來,這些對象中的一部分就可能是導致內存泄漏的對象。通用的流程為:
打開應用,初始化場景(觸發GC)-> 拍攝第一次Snapshot作為基準 -> 多(N)次觸發內存泄漏操作 -> 拍攝第二次堆快照 -> 觸發主動GC-> 拍攝第三次堆快照
由于方舟虛擬機提供了在獲取堆快照之前自動GC的功能,因此我們可以將上述流程簡化為兩步,同時加上Profiler的錄制功能,整體流程為:
打開應用,初始化場景-> 開啟錄制Snapshot Insight-> 拍攝第一次Snapshot作為基準 -> 多(N)次觸發內存泄漏操作 -> 拍攝第二次堆快照-> 結束錄制
錄制完成后,會得到如下圖所示的數據
錄制過程中,我們采集了兩次堆快照,對應在Profiler的界面上就是兩個紫色的條塊,每一個條塊內的數據都是當前的虛擬機堆快照。條塊上的數字大小代表的是虛擬機堆內存的實際占用。
由于在每次拍攝堆快照之前,虛擬機都會觸發GC,所以理論上堆快照內存在的對象都是當前虛擬機已經無法GC掉的對象,所以我們可以將兩個堆快照進行比較,來查看哪些對象是我們在觸發問題場景時新增了且不能釋放的。
點擊Snapshot Insight面板的Comparison頁簽,將兩次Snapshot進行比較,如下圖。圖中數據的含義為以Snapshot2作為基準,Snapshot2對比Snapshot1的數據變化量。
即便是比較視圖,東西也非常多,怎么分析呢?
還記得上面說的操作N次嗎,在觸發內存問題場景時將問題觸發N次,在比較視圖中首先就去找與N強相關、與業務代碼強相關的constructor,首先來分析這些對象是否正常。
首先介紹一下Snapshot比較視圖中各項數據的含義,如下圖,更加具體的也可以參考內存泄露分析或者使用Snapshot Insight分析ArkTS內存問題。
內存泄漏分析:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/ide-insight-session-snapshot-V13?catalogVersion=V13
使用Snapshot Insight分析ArkTS內存問題:https://developer.huawei.com/consumer/cn/forum/topic/0207154775311720043?fid=0109140870620153026
在找到相關的業務關聯的對象后,可以從references里面一層層去尋找、排查在引用鏈上的可疑對象(一般指與業務代碼關聯的對象,尤其是xxx in com.xxx.yyy這種明顯是業務使用到的對象的位置)。
在排查時,可以主要往兩個方向:
1)Distance逐漸減小;
2)引用鏈上都是業務相關的對象。
從這兩個維度去分析入度引用鏈,找出那些業務邏輯上應該釋放但是實際并沒有釋放的對象及其引用關系,從這些引用關系上來排查是否有不合理的強引用導致的內存無法釋放的問題,進而解決內存泄漏問題。
具體的案例可以參考:使用SnapshotInsight分析ArkTS內存問題
使用Snapshot Insight分析ArkTS內存問題:https://developer.huawei.com/consumer/cn/forum/topic/0207154775311720043?fid=0109140870620153026
2.2 分析Native內存
其次針對Native Heap,由于該部分堆內存僅由開發者自行控制分配釋放,無法使用類似虛擬機的dump手段來分析整體的堆內存使用情況,僅可使用Profiler抓取到錄制過程中的應用內存分配和釋放事件,因此需要開發者通過內存分配/釋放事件以及內存分配棧等信息自行找到代碼中的內存申請和釋放點來確認是否存在申請、釋放不配對導致的內存泄漏問題。
內存分析模板Allocation提供了該能力,使用該模板可以抓取到經由系統基礎庫分配的Native部分內存,分析分配/釋放的匹配邏輯,界面如下圖。
在開始錄制前,需要先了解一些該模板的工作原理以及相關的配置參數,以確定能夠抓取到應用中可能存在的內存問題。
Allocation模板通過hook基礎庫中的某些函數調用來獲取每次內存分配的數據,并將這些數據返回至Profiler中,在Profiler中完整的展示這些內存的分配、釋放數據以及相關的調用棧、庫等信息。詳細的使用介紹可以參考這里內存分析及優化,下面就具體的使用流程做介紹。
內存分析及優化:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/ide-insight-session-allocations-memory-V13?catalogVersion=V13
2.2.1Native數據采集
首先,是錄制前的參數配置,最新版本的配置頁面如下,其中的配置項都是針對NativeAllocation這條泳道的,下面依次介紹:
1)Statistics Mode、Sampling Interval:該項配置代表是否開啟統計模式采集數據,默認開啟。開啟后,數據會每隔SamplingInterval中設置的時間從設備端匯總并返回,該模式下工具的采集性能會更好、負載更低、可采集的時間也更長,但是會丟失掉精準的每次分配釋放的內存信息;關閉該模式,會開啟詳情采集模式,返回的數據中會給出每次內存分配的詳細情況,包含內存分配地址、分配大小、分配棧等信息。推薦使用統計模式,如業務側有需要也可酌情使用詳情模式。
2)Filter Size:過濾內存大小。該參數表示最小抓取的內存大小,默認為1024bytes(1kb),內存分配時小于該大小的內存分配信息不會被抓取到,應用可根據自身具體情況選擇該參數。該參數可能會顯著影響應用性能,當該值過小且應用在分配大量內存的場景時(抓取的內存分配數據量巨大)可能會造成應用卡頓,因此建議選擇合適的參數。
3)Backtrace Mode:內存分配棧回棧方式,默認FP回棧。FP回棧性能更好,默認開啟,但在某些特定場景下(例如so的編譯參數控制),FP回棧可能失效,此時可選擇DWARF回棧嘗試。
4)Record JS Stack:是否開啟JS回棧。當該開關打開時,系統回棧時會自動從Native向JS層回棧,完成Native到JS的棧縫合,適合ArkTS/JS代碼調用Native的場景。
5)JS Backtrace Depth、Native Backtrace Depth:內存回棧深度。代表最大的回棧層數,層數越大對性能開銷越大,可結合業務動態調整。
注:當選擇了DWARF回棧后,JS和Native的回棧深度會變成一個框Backtrace Stack,其層數代表著JS與Native的共同回棧深度。
做好配置后,可以開始抓取問題場景的數據,切記需要在保存數據并開啟錄制之后,操作應用復現問題場景,并在問題復現完成后停止錄制。(注:如果是使用統計模式,錄制的結束時間需要是SamplingInterval即采集周期的整數倍,例如當采集周期是10s時,停止時間建議在11s+/21s+/31s+,以此類推,留出余量給系統做數據處理與傳輸)。錄制完成后界面類似下圖。
其中泳道部分展示了當前的內存變化情況,Native Allocation泳道下方又涉及兩個子泳道AllHeap和All Anonymous VM,這兩個泳道分別代表使用malloc和mmap函數分配的內存情況,下方的詳情區域展示對應泳道的內存分配統計數據(Statistics頁簽)與內存分配棧(CallTrees)信息。
2.2.2Native數據分析
抓取到了問題場景的數據,接下來就是對問題數據的分析。
首先,框選范圍內的數據展示的是:在框選范圍的起點之后及在框選范圍的終點之前的所有內存分配的數據,這個邏輯很重要,會對分析結論有很大影響,需重點關注。
接下來,在詳情面板的左下角有一個下拉單選框,能夠篩選當前詳情區域展示的內存的數據,如下圖所示:
1)All Allocations:框選的時間段的所有分配內存信息。
2)Created & Existing:在框選范圍的起點之后分配的,且在框選范圍的終點之前沒有釋放的內存數據。
3)Created & Released:在框選范圍的起點之后分配的,且在框選范圍的終點之前已經釋放的內存數據。
這三個選項可以根據具體的業務問題和訴求來確定。
第二個框中的Native Size和Native Library代表可以通過這兩個維度:大小和分配庫的信息來做統計,一個是按照分配大小聚合、另一個是按照分配該內存的庫來聚合。
通過這個頁簽的統計信息、對未釋放的數量、大小等列排序后,能夠大致分析出內存出現問題的情況以及場景,為分析內存棧做一些提前準備。接下來可以把數據切換到CallTrees內存分配棧上。
該部分數據展示了詳細的內存分配棧信息,內存問題一般都是在該部分通過棧幀信息找出相關的業務邏輯來定位。其中有兩個點需要說明:
1)棧幀中主要為Native棧,除了應用本身編譯的一些so及帶有部分接口信息的so信息外,其他系統庫部分僅展示so庫與函數偏移信息,若需要查看這部分信息,需要導入相應版本的帶符號的so庫。具體參考基礎耗時分析中的離線符號解析小節。
基礎耗時分析:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V13/ide-insight-session-time-V13?catalogVersion=V13
2)開啟Record JS Stack之后,部分棧幀中可能包含JS棧信息,JS棧信息一般對應著應用源碼,雙擊能夠跳轉到代碼行上。
注:所有Category上有高亮色的棧幀信息,都可以通過雙擊跳轉到源代碼所在的文件和代碼行上(前提是打開的工程是調優應用的相匹配的工程)。
同樣地,在分析調用棧的過程中,可以使用下方操作欄上的一系列功能,除了上述介紹過的AllAllocations篩選能力之外,在Native Allocation泳道的Call Trees頁簽中,可以通過底部的“Call Trees”和“Constraints”選擇框來過篩選和過濾內存分配棧。
Call Trees選擇框包含兩種過濾條件:
Separate byAllocated Size:在內存分配棧完全相同的情況下,會按照每次分配棧申請的內存大小將棧分開;
Hide SystemLibraries:隱藏內存分配棧中的系統堆棧。
Constraints選擇框也包含了兩種過濾條件:
Count:根據指定的內存申請次數過濾內存分配棧信息;
Bytes:根據指定的內存申請大小過濾內存分配棧信息。
業務方可以根據自身的業務情況與具體的問題場景來適當使用這些數據篩選能力,同時在下方也提供搜索功能,InvolvesSymbol Name搜索框提供了針對函數調用棧的文本搜索能力,可以快速搜索棧幀信息與so庫信息并快捷跳轉至對應位置。
另外,若表格的樹狀圖不便于直觀看出內存信息,可以使用Flame Chart火焰圖開關,使用火焰圖的形式來分析內存分配可能存在的問題,如下圖,火焰圖中的條塊長度越長,代表該調用棧分配的內存大小越大。
2.2.3Native內存問題分析
在前面兩個小節介紹了整個Native Allocation的使用及分析方式,這里簡單介紹具體的問題分析邏輯:
1. 錄制問題場景的內存分配信息;
2. 從統計信息及內存分配棧查看在當前范圍內未釋放的內存信息(選擇Created &Existing),通過2.2.2節中介紹的各種分析和篩選能力找出未釋放的內存堆棧,并將該堆棧結合業務邏輯分析;
3. 若涉及ArkTS代碼對Native的調用邏輯,可開啟Record JS Stack開關,將Native的內存棧回棧至JS層,簡化分析難度;
4. 通過這些問題棧幀信息映射到業務代碼中,結合問題場景和代碼分析為何該部分內存未釋放,找到問題點。
修改及驗證
當經過上述步驟的流程分析完應用的內存問題之后,基本上已經可以定位到問題發生的位置及相關的代碼段,在此基礎上結合業務邏輯對代碼做修改,修改后重新編譯推包到真機上,在相同的場景下嘗試復現問題,并使用實時監控或者Allocation/Snapshot模板的Memory泳道來監測應用內存占用情況,以確認問題是否還存在。
總結
問題不是一朝一夕出現的,通常問題會在開發的過程中逐漸積累,到最終暴露出來時可能已經涉及了多個模塊、多種邏輯,各種邏輯互相耦合,導致分析的難度大大增加。
這種情況下,我們建議把性能相關的工作也能做到平時,在開發過程中也去關心程序的性能問題。例如,剛寫了一段很長的引用關系、增加了一些注冊實例的邏輯或者做了一些組件間的變量傳遞,這種時候就可以去結合邏輯自己設想一下,會不會引發一定的性能問題,甚至可以在平時就用調優工具來檢測一把。
這樣做到每個開發階段都保證了性能的可靠,那么在項目日益增大的同時,性能問題也不會嚴重到離譜、無法分析。
附錄
DevEco Studio下載鏈接:https://developer.huawei.com/consumer/cn/deveco-studio/
DevEco Profiler官方指導文檔:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-insight-V5
-
內存
+關注
關注
8文章
3042瀏覽量
74177 -
操作系統
+關注
關注
37文章
6862瀏覽量
123503 -
鴻蒙
+關注
關注
57文章
2381瀏覽量
42940 -
DevEco Studio
+關注
關注
0文章
25瀏覽量
1121
原文標題:如何使用DevEco Studio性能調優工具Profiler定位應用內存問題
文章出處:【微信號:HarmonyOS_Dev,微信公眾號:HarmonyOS開發者】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論