引言:什么是内存泄漏?

想象一下你的手机是一个酒店,每个应用程序都是酒店的客人。当客人(应用程序)使用房间(内存)时,酒店经理(系统)会分配房间给他们使用。正常情况下,客人退房(应用关闭)后,房间应该被清理并重新可用。

内存泄漏就像是客人离开了酒店却忘了退房,房间一直被占用无法重新分配。随着时间推移,被占用的房间越来越多,最终酒店没有空房可供新客人使用——这就是应用程序变慢甚至崩溃的原因。

先复习一下前端开发中的内存泄漏

常见的前端内存泄漏场景

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…