鴻蒙中內存泄漏分析

引言:什么是內存泄漏?

想象一下你的手機是一個酒店,每個應用程序都是酒店的客人。當客人(應用程序)使用房間(內存)時,酒店經理(系統)會分配房間給他們使用。正常情況下,客人退房(應用關閉)后,房間應該被清理并重新可用。

內存泄漏就像是客人離開了酒店卻忘了退房,房間一直被占用無法重新分配。隨著時間推移,被占用的房間越來越多,最終酒店沒有空房可供新客人使用——這就是應用程序變慢甚至崩潰的原因。

先復習一下前端開發中的內存泄漏

常見的前端內存泄漏場景

1. 意外的全局變量
// 錯誤示例:意外創建全局變量
function createLeak() {leakedData = '這是一個全局變量'; // 沒有使用var/let/const
}// 正確做法
function noLeak() {const safeData = '局部變量'; // 使用const或let
}
2. 未清理的定時器和回調函數
// 可能引發內存泄漏
setInterval(() => {// 某些操作
}, 1000);// 正確做法:保存引用并適時清理
const intervalId = setInterval(() => {// 某些操作
}, 1000);// 在組件卸載時清理
clearInterval(intervalId);
3. DOM引用未釋放
// 保存DOM引用
const elements = {button: document.getElementById('myButton'),container: document.getElementById('myContainer')
};// 即使從DOM移除,JavaScript仍保留引用
document.body.removeChild(document.getElementById('myContainer'));// 需要手動釋放引用
elements.container = null;
4. 閉包使用不當
// 可能引起內存泄漏的閉包
function createClosure() {const largeData = new Array(1000000).fill('*');return function() {// 閉包持有largeData引用,即使外部函數已執行完畢console.log('閉包被調用');};
}// 正確使用:避免不必要的閉包引用
function safeClosure() {return function() {// 不引用外部變量console.log('安全的閉包');};
}

前端內存泄漏檢測工具

  1. Chrome DevTools

    • Memory面板:生成堆快照(Heap Snapshot)

    • Performance面板:記錄內存分配情況

    • Allocation instrumentation on timeline:跟蹤內存分配

使用示例

// 手動觸發垃圾回收(僅在DevTools中有效)
if (window.gc) {window.gc();
}

鴻蒙應用中的內存泄漏

鴻蒙內存管理特點

鴻蒙系統使用方舟編譯器和高性能的運行時環境,但仍然需要開發者注意內存管理。

常見的鴻蒙內存泄漏場景

1. 未取消注冊的回調

javascript

// 注冊回調
appContext.registerReceiver(receiver, intentFilter);// 必須適時取消注冊
appContext.unregisterReceiver(receiver);
2. 資源未正確釋放

javascript

// 使用資源
const pixelMap = await image.createPixelMap(byteBuffer);// 使用完畢后必須釋放
pixelMap.release();
3. 組件引用未清理

javascript

// 自定義組件中
@Component
struct MyComponent {private controller: VideoController = new VideoController();// 必須實現aboutToAppear和aboutToDisappearaboutToDisappear() {// 清理資源this.controller.release();}
}
4. 異步任務未取消

javascript

// 啟動異步任務
const task = new MyAsyncTask();
task.execute();// 在組件銷毀時取消任務
aboutToDisappear() {if (task) {task.cancel();}
}

鴻蒙應用中使用工具來檢測

初步識別內存問題

  1. 使用實時監控功能(詳細使用方法請參考性能問題定界:實時監控)對應用的內存資源進行監控。正常操作應用,觀察運行過程中的應用內存變化情況。當在一段時間內應用內存沒有明顯增加或者在內存上漲后又逐漸回落至正常水平,則基本可以排除應用存在內存問題;反之,在一段時間內不斷上漲且無回落或者內存占用明顯增長超出預期,那么則可初步判斷應用可能存在內存問題。

  2. 當從實時監控頁面初步判斷應用可能存在內存問題后,可以使用Memory泳道來抓取應用內存在問題場景下的詳細數據以及變化趨勢,初步定界問題出現的位置。Memory泳道存在Allocation或Snapshot模板中,使用Allocation或Snapshot模板錄制均可。
  3. 創建模板后,將模板中的其余泳道去除勾選,僅錄制Memory泳道的數據。

    說明

    其余泳道會開啟對內存分配、內存對象等數據的抓取,這些功能會帶來額外的開銷,可能會對我們初步定界問題產生噪音,影響分析,故先排除錄制。

  4. 點擊三角按鈕即開始錄制。
  5. 錄制過程中,不斷操作應用在問題場景的功能,將問題放大,便于快速定界問題點。
  6. 點擊下圖中方塊按鈕或者左側停止按鈕結束錄制。

  7. 錄制完成后,展開Memory泳道,其中ArkTS Heap表示方舟虛擬機內存,這部分內存受到方舟虛擬機的管控。Native Heap表示Native內存,主要是應用使用到的一些涉及Native的API所申請的內存以及開發者自己的Native代碼所申請使用的堆內存(通常是C/C++),這部分內存需要開發者自行管理申請和釋放。

    當ArkTS Heap有明顯的上漲,說明在方舟虛擬機內的堆內存上可能存在內存泄漏,可以使用Snapshot模板進行下一步分析;當Native Heap有明顯的上漲,說明Native內存上可能存在內存泄漏,可以使用Allocation模板進行下一步分析。

使用Snapshot模板分析ArkTS內存問題

分析步驟

分析內存泄漏問題步驟如下:

  1. 使用Snapshot模板錄制數據;
  2. 在問題場景前拍攝快照;
  3. 觸發問題場景后,再次拍攝快照;
  4. 對比兩次快照的數據,可快速找到泄漏對象并做進一步分析;
  5. 當有多個對象在比較視圖都存在時,可以重復多次觸發問題場景后拍攝快照,分別和問題場景前拍攝的快照進行對比,觀察是否有對象出現明顯的線性變化趨勢,進一步縮小泄漏對象的范圍。

錄制Snapshot模板數據

  1. 連接設備后啟動應用,點擊應用選擇框選擇需要錄制的應用,選擇Snapshot模板,點擊Create Session或雙擊Snapshot圖標即可創建一個Snapshot的錄制模板。

  2. 創建模板后,點擊三角按鈕即開始錄制。

  3. 待右側泳道全部顯示recording后則表明正在錄制中。

  4. 拍攝第一次堆快照作為基準(點擊圖中①處拍攝按鈕,待②處顯示出紫色條塊表示快照拍攝完成)。

    說明

    方舟虛擬機提供了在獲取快照前自動GC(Garbage Collection,對堆內存進行垃圾回收)的能力,因此拍攝快照之前不用主動觸發GC。

  5. 多次觸發內存泄漏操作。可以操作5,7,11等這種特殊的次數。比如操作了5次對比兩個快照發現有很多創建了5次沒釋放的場景,則可能存在內存泄漏,再操作7次,如果創建了7次那就可以確認發生了泄漏。
  6. 拍攝第二次堆快照。
  7. 點擊下圖中方塊按鈕或者左側停止按鈕結束錄制。

分析ArkTS Heap

  1. 在每次拍攝堆快照之前,虛擬機都會觸發GC,所以理論上堆快照內存在的對象都是當前虛擬機已經無法GC掉的對象。我們可以將兩個堆快照進行比較,來查看哪些對象是在觸發問題場景時新增了且不能釋放的。切換到窗口下方詳情區域的“Comparison”頁簽,將兩次快照進行對比。圖中數據的含義是以Snapshot2作為基準,Snapshot2對比Snapshot1的數據變化量。

  2. 優先尋找與觸發內存泄漏操作次數強相關、與業務代碼強相關的Constructor,首先來分析這些對象是否正常。主要是按照Distance逐漸減小的方式找引用鏈,可以從references里面一層層去尋找,排查引用鏈上的可疑對象(一般指與業務代碼關聯的對象)。

    說明

    選擇一個實例結點,底部搜索欄的Path to GC Root按鈕成可點擊狀態。點擊該按鈕,系統會計算從GC Roots垃圾收集器根到選定實例對象的最短路徑(最短路徑是指Distance逐漸-1的路徑,最終抵達Distance = 1的結點),并在右側區域展示。

分析Snapshot數據

常見對象介紹

JSArray

目前所有JSArray展開后為數組里的各個元素:

其中__proto__:原型對象,所有數組的__proto__應該是一致的;length:內置屬性訪問器,可以訪問數組長度。

TaggedDict

位于(array)標簽中,一般為虛擬機內部創建的字典,ArkTS代碼層面不可見。

TaggedArray

位于(array)標簽中,一般為虛擬機內部創建的數組,ArkTS代碼層面不可見。

COWArray

位于(array)標簽中,一般為虛擬機內部創建的數組,ArkTS代碼層面不可見。

JSObject

JSObject展開后為內部的各個屬性如下:

以下通過具體代碼來介紹下實例化對象、聲明對象、構造函數間的關系:

 
// HelloWorldPage.ets
class People {
old: number
name: string
constructor(old: number, name: string) {
this.old = old;
this.name = name;
}
printOld() {
console.log("old = ", this.old);
}
printName() {
console.log("name = ", this.name);
}
}@Entry
@Component
struct HelloWorldPage {
@State message: string = 'Hello World';
private people: People = new People(20, "Tom");build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
}
.height('100%')
}
}

采集到的snapshot數據如下:

202169對象對應的是People,其主要聲明了對象的屬性和方法。

實例化對象的__proto__屬性指向聲明時的對象,聲明對象里則會有constructor構造函數。當實例化多個對象時,實例化對象會有多個,但是聲明對象和構造函數只有一個。

JSFunction

目前所有JSFunction都在(closure)標簽中,展開即可看到所有JSFunction:

每個函數展開后為函數內的各個屬性:

其中HomeObject表示父類對象,即該方法屬于哪個對象;_proto_表示原型對象;LexicalEnv表示該函數的閉包上下文;name是內置屬性訪問器,可獲取函數名;FunctionExtraInfo表示額外信息,比如一些napi接口會在這里記錄函數地址;ProtoOrHClass表示原型或者隱藏類。

如果函數顯示為anonymous(),則表示為匿名函數;如果函數顯示為JSFunction(),則表示該函數可能為框架層函數,創建函數的時候未設置函數名。對于這兩種函數名不可見的情況,可以通過查看其引用來間接確認其名稱:

ArkInternalConstantPool

虛擬機創建的常量池,ArkTS代碼層面不可見,涉及到的字符串常量會在(array)標簽中展示:

LexicalEnv

閉包變量上下文;閉包是一個鏈狀結構,如下所示:

733這個節點本身是一個閉包數組,其中0號元素是調用者(或者再往上的調用者,以此類推)的閉包;1號元素存儲的是調試信息;2號及以后的元素存儲的就是閉包傳遞的變量,上例傳遞了一個變量。

InternalAccessor

內置屬性訪問器,會有getter和setter方法,通過getter、setter可以獲取、設置該屬性。

分析方法

查看對象名稱

對于聲明對象,可以通過constructor屬性來確定對象名稱。

對于實例化對象,一般沒有constructor,則需要展開__proto__屬性后查找constructor;

若對象里有一些標志性屬性,可以通過在代碼里搜索屬性名稱來找到具體是哪個對象。

如果對象間有繼承關系,則可以繼續展開__proto__:

總結

內存泄漏就像是軟件中的"慢性病",初期不易察覺,但長期積累會導致嚴重性能問題。無論是前端開發還是鴻蒙應用開發,都應該:

  1. 提高意識:認識到內存管理的重要性

  2. 遵循最佳實踐:使用正確的編程模式和API

  3. 定期檢查:使用工具檢測潛在的內存問題

  4. 及時修復:發現內存泄漏立即解決

通過良好的內存管理習慣,可以顯著提升應用性能,提供更流暢的用戶體驗。記住,預防總是比治療更有效,在編寫代碼時就考慮到內存管理,可以避免后期大量的調試和優化工作

華為開發者學堂

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/94227.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/94227.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/94227.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

將windows 的路徑掛載到Ubuntu上進行直接訪問

1、下載hane NFS Server安裝2、安裝后打開3、在電腦上創建個共享文件夾,我這里選擇D:\share4、在hane win nfs server 軟件上選擇Edit\preferences5、選擇exports6、選擇Edit exports file, 在最后添加D:\share -name:nfs,然后點擊Save如果添加root權限使…

開源 python 應用 開發(十一)短語音轉文本

最近有個項目需要做視覺自動化處理的工具,最后選用的軟件為python,剛好這個機會進行系統學習。短時間學習,需要快速開發,所以記錄要點步驟,防止忘記。 鏈接: 開源 python 應用 開發(一&#xf…

【C++闖關筆記】封裝②:友元與模板

系列文章目錄 第零篇:從C到C入門:C有而C語言沒有的基礎知識總結-CSDN博客 第一篇:【C闖關筆記】封裝①:類與對象-CSDN博客 第二篇:【C闖關筆記】封裝②:友元與模板-CSDN博客 第三篇:【C闖關筆…

Python 爬蟲教程 | 豆瓣 TOP250 數據抓取與分析實戰

一、項目背景與數據價值豆瓣TOP250是影視行業的重要榜單,具有以下數據價值:評分與評價人數:衡量電影市場熱度;導演與演員信息:分析人才價值與影視趨勢;類型 / 地區 / 年份:洞察電影類型與年代變…

第04章 SPSS簡介與數據庫構建

參考:SPSS實戰與統計思維 - 武松編著 - 微信讀書 4.1 SPSS簡介 發展歷史 全稱Statistical Product and Service Solutions,由美國斯坦福大學三位研究生于1968年開發。 對比其他軟件成立時間:SAS(1976年)、Stata&…

【ABAP4】數據字典

ABAP數據字典ABAP數據字典概述數據字典的基本對象域數據元素表類型系統創建自定義透明表創建自定義結構鎖對象ABAP數據字典概述 ABAP數據字典是SAP定義和管理數據的工具,包含了程序使用的所有對象,數據字典中包括數據庫表、視圖、數據類型、域、搜索幫助…

不知道Pycharm怎么安裝?Pycharm安裝教程(附安裝包)

Pycharm安裝教程(附安裝包)獲取方式:python開發工具包丨夸克網盤-資源免費下載 有位朋友剛開始學習python,不知道Pycharm要怎么安裝,于是問我要一個安裝教程。 先介紹一下Pycharm吧,PyCharm是一款python開…

在 Docker 容器中查看 Python 版本

博客目錄前言方法一:交互式進入容器查看方法二:啟動時直接執行命令方法三:啟動后使用 exec 執行命令方法四:直接運行并查看版本(容器退出)方法比較與選擇指南實際應用中的注意事項進階技巧批量檢查多個鏡像…

React:Umi + React + Ant Design Pro的基礎上接入Mock數據

為什么需要Mock數據 前端開發依賴后端接口時的阻塞問題 獨立開發和測試的需求 快速迭代和原型驗證的重要性 當前版本及框架 React18 Umi 4.0 Ant Design Ant Design Pro 其實這些都不重要,主要是有Umijs,因為Umijs具有開箱即用Mock功能的能力&#…

VMware centos磁盤容量擴容教程

目錄前言相關概念磁盤磁盤分區文件系統掛載點物理卷、VG(卷組)、LV(邏輯卷)、LVM(邏輯卷管理)解決方案前言 這篇博客主要分享我在VM中通過docker搭建dify大模型應用平臺時,遇到了分配的磁盤容量…

kubernetes中的認證和授權

一 kubernetes API 訪問控制Authentication(認證)認證方式現共有8種,可以啟用一種或多種認證方式,只要有一種認證方式通過,就不再進行其它方式的認證。通常啟用X509 Client Certs和Service Accout Tokens兩種認證方式。…

雅菲奧朗SRE知識墻分享(四):『AI已開始重塑勞動力市場,美國年輕科技從業者首當其沖』

近日,據《商業內幕》報道,AI正在重塑美國就業市場,年輕的科技從業者正首當其沖地感受到沖擊。高盛首席經濟學家Jan Hatzius在本周一撰文指出:“AI 確實開始在各類數據中顯現出更加明顯的跡象。”據高盛的分析,科技行業…

Python爬蟲入門指南:從零開始的網絡數據獲取之旅

文章目錄前言1. 什么是網絡爬蟲?2. 爬蟲的倫理與法律邊界3. Python爬蟲的基本工具庫3.1 Requests:HTTP請求庫3.2 Beautiful Soup:HTML/XML解析庫3.3 lxml:高效XML/HTML解析器3.4 Selenium:自動化瀏覽器工具4. 第一個爬…

說說你對JVM的垃圾回收機制的理解?

Java 虛擬機(JVM)的垃圾回收(Garbage Collection,GC)機制是自動管理內存的核心,其核心目標是識別并回收不再被使用的對象所占用的內存,避免內存泄漏和溢出。以下從垃圾判斷方法、垃圾回收算法和…

兌換汽水瓶

實現代碼:public static void main(String[] args) {Scanner in new Scanner(System.in);while (in.hasNextInt()) {int n in.nextInt();if (n 0) {break;}System.out.println(n / 2);}}

結合 Flutter 和 Rust 的跨平臺開發方案

結合 Flutter 和 Rust 的跨平臺開發方案 1. 核心思想 本方案的核心思想是Flutter 負責 UI,Rust 負責邏輯 。Flutter 作為一個成熟的 UI 框架,專注于渲染流暢、跨平臺一致的用戶界面。而將那些對性能、安全和并發有高要求的復雜業務邏輯、計算密集型任務或底層系統操作,全部…

理想汽車智駕方案介紹 2|MindVLA 方案詳解

一、引言 MindVLA 主要包括空間智能模塊、語言智能模塊、動作策略模塊、強化學習模塊,這些模塊分別有以下功能: 空間智能模塊:輸入為多模態傳感器數據,使用 3D 編碼器提取時空特征,然后將所有傳感器與語義信息融合成…

計算機網絡基礎(三) --- TCP/IP網絡結構(運輸層)

運輸層1. 概述和運輸服務運輸層協議為運行在不同主機上的應用進程之間提供了邏輯通信功能, 運輸層協議是在端系統中而不是路由器中實現的, 網絡應用程序可以調用多種運輸層協議, 如因特網的兩種協議: TCP 和 UDP ,每種協議都能為調用的應用程序提供一組不同的運輸層服務1.1 運輸…

JdbcTemplate和MyBatis的區別

在 Java 后端開發中,JdbcTemplate(Spring 框架提供)和 MyBatis(持久層框架)都是用于簡化數據庫操作的工具,但它們的設計理念、使用方式、靈活性和適用場景有顯著差異。下面從核心定位、核心特性、使用方式、…

埃氏篩|樹dfs|差分計數

lc525把數組里的0換成-1&#xff0c;求子數組和為零的最長長度用哈希表記錄前綴和首次出現的位置通過找相同前綴和的位置差得出最長的0和1數量相等的子數組長度。class Solution { public:int findMaxLength(vector<int>& nums) {unordered_map<int,int>hashta…