我的编程空间,编程开发者的网络收藏夹
学习永远不晚

怎么排查Javascript内存泄漏

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

怎么排查Javascript内存泄漏

这篇文章主要讲解了“怎么排查Javascript内存泄漏”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么排查Javascript内存泄漏”吧!

如何判断我的应用发生了内存泄漏

为了证明螃蟹的听觉在腿上,一个专家捉了只螃蟹并冲它大吼,螃蟹很快就跑了。然后捉回来再冲它吼,螃蟹又跑了。最后专家把螃蟹的腿都切了,又对着螃蟹大吼,螃蟹果然一动不动……

定位内存问题的过程其实也类似,如果你自己都不知道自己的页面在使用过程中哪些步骤会导致内存增长,那很可能就会错把一个正常的内存增长当作内存泄漏来排查,最后查了半天白忙活。 其实一个单页应用在使用过程中,内存发生增长是很合理的。例如在开发过程中,为了优化使用体验,我们可能会对部分数据进行缓存,这部分缓存的数据其实也会导致内存占用的升高,但它是符合预期的。因此,排查内存泄漏的第一步,就是要先梳理一遍自己的代码,看一下哪部分内存的升高是合理的,哪部分内存的升高是不合理的。

Performance和Memory都可以用来定位内存问题,先用谁呢

答案是先用Performance。 当我们怀疑页面发生了内存泄漏的时候,可以先用Performance录制一段时间内页面的性能变化。你只需要切换到Performance面板,点击Record,然后在页面上正常操作一段时间,最后停止录制即可。

怎么排查Javascript内存泄漏

不断升高的内存下限

如果录制结束后,看到内存的下限在不断升高的话,你就要注意了 —— 这里有可能发生了内存泄漏。

除了内存增长曲线,Nodes(Dom节点数曲线)、Document曲线以及Listener曲线也同样值得关注,有时候它们对内存问题的定位也很有帮助。

当你怀疑发生了内存泄漏的时候,你就可以用Memory面板来进一步定位泄漏的源头了。

通过Memory面板定位内存泄漏的流程通常是怎么样的呢

通常,我们可以从Memory的主界面开始,点击左上角的圆点就可以记录下当前的堆内存快照(heap snapshot)了。

怎么排查Javascript内存泄漏

Memory面板

这里推荐一个Gmail团队也在用的 “three snapshot”技巧:

  • 打开DevTools, 切换至Memory面板

  • 先记录一个堆内存快照

  • 在你的页面上执行可能发生泄漏的操作

  • 再记录一个堆内存快照

  • 重复执行多几遍步骤3

  • 最后记录一个堆内存快照

  • 选择最后一个堆内存快照,找到顶栏的“All objects”, 切换至”Objects allocated between snapshots 1 and 2”(也可以对2,3执行同样的操作)

怎么排查Javascript内存泄漏

过滤出两份快照之间新分配的对象

切换后,你就能看到两个快照之间新生成的对象。你可以选择其中一项点开,看看它的retaining tree里面保留了哪些对象没有释放。

Tips:在记录第一个堆快照之前你可以先做一些“预热”操作,避免一些懒加载和缓存策略影响到了对内存的分析。

为什么我的内存快照记录下来之后看不懂,还出现了很多奇怪的变量

这也是我排查内存泄漏时遇到的第一个问题,为什么教程里的内存快照简洁易懂,我的内存快照却像一本天书?

怎么排查Javascript内存泄漏

教程里的内存快照

怎么排查Javascript内存泄漏

我的内存快照

为什么有这么大的差异呢?除去教程里demo代码比较简单之外,提前准备好一个合理的debug环境也是很重要的。这里我列举了4点个人觉得对debug内存问题很有帮助的措施:

尽量使用没有混淆的代码:

打包后的代码往往经过了混淆和压缩,在生产环境上这是必要的,但在debug时却会成为我们的绊脚石,不便于阅读。

排查问题时使用production模式编译出来的代码:

Dev模式下往往会开启一些方便开发的特性,例如热更新等。但它们可能会占用一部分的内存,影响到内存问题的排查,所以建议还是使用production模式编译出来的代码进行问题排查。

屏蔽所有浏览器插件:

屏蔽浏览器插件最快的方式就是打开无痕窗口。浏览器插件给我们带来很多便利,但插件注入的额外逻辑有时也会影响内存问题的排查。例如vue-devtools会记录下每一个vuex mutaions,导致内存无法释放。

在现场打内存快照,便于跳转到源代码所在行:

尽管devTools记录下来的内存快照文件可以单独加载展示,但还是建议在记录下内存快照的时候“趁热”分析,因为这时还能从retaining tree上跳转到代码所在行,有时候对定位问题也很有帮助。

怎么排查Javascript内存泄漏

跳转到源码所在行

快照里有一些“Detached DOM tree”,是什么意思

一个DOM节点只有在没有被页面的DOM树或者Javascript引用时,才会被垃圾回收。当一个节点处于“detached”状态,表示它已经不在DOM树上了,但Javascript仍旧对它有引用,所以暂时没有被回收。通常,Detached DOM tree往往会造成内存泄漏,我们可以重点分析这部分的数据。

Shallow size 和 Retained size,它们有什么不同

Shallow size: 这是对象自身占用内存的大小。通常只有数组和字符串的shallow size比较大。

Retain size: 这是将对象本身连同其无法从 GC 根到达的相关对象一起删除后释放的内存大小。 因此,如果Shallow Size = Retained Size,说明基本没怎么泄漏。而如果Retained Size > Shallow Size,就需要多加注意了。

Memory里的Summary视图, Comparison视图, Dominators视图和Containment视图分别有什么不同呢

Summary view:

顾名思义,Summary view就是当前内存快照的一个概览。我们先介绍一下这个视图下的每一列是什么意思: - Constructor: 对象的构造器。 - Distance:与root的距离。距离越大,处理和加载这个对象的时间就越长。 - Object Count:指定构造器创建的对象的数量。 - Shallow Size:对象自身占用内存的大小。 - Retained Size:释放掉该对象后,能释放掉的内存。

在这个视图下你可以看到当前页面内存的具体构成,但如果想定位内存问题,下面的Comparison view会更加有用。

Comparison view:

Comparison视图可以让你对比两份内存快照之间的差异。默认是跟上一份快照做对比,当然你也可以选择任意两份内存做对比。这个视图下每一列的数据有点不同: - Constructor: 对象的构造器。 - # New: 该对象构造器下有多少新对象被创建 - # Deleted: 该对象构造器下有多少新对象被销毁 - # Delta: # New - # Delete的差值 - Alloc.Size:两份快照之间新分配的内存 - Freed Size: 两份快照之间释放掉的内存 - Size Delta:Alloc Size - Freed Size 的差值

这个视图绝对是排查内存泄漏的利器。当你能定位到是哪些操作可能造成内存泄漏后,比较操作前后的内存快照,很容易就能发现发生内存泄漏的对象。

Containment view:

Containment view提供了一个自下而上的视图,它允许你浏览和探索堆内存的内容。我们可以用它来分析一些全部变量的引用情况(如window)。

Statistics view:

Statistics视图会用饼图的形式展示各个类型对象的内存占比

Constructor下的(array), Array, (closure), (compiled code)都对应的哪些内容?

  • (closure): 函数闭包持有的内存引用。

  • (array, string, number, regex): 包含着一系列对象,这些对象的属性上有对应类型变量的引用。

  • (compiled code): Javascript引擎(如V8)为了加快运行速度,会对代码进行一次编译。(compiled code)顾名思义就是指与编译后的代码相关联的内存。

  • Detached HTMLDivElement等:代码里对指定类型Dom节点的引用。

发现有一个叫feedback_cell的字段经常出现,它是什么?是它导致了内存泄漏吗?

怎么排查Javascript内存泄漏

经常出现的feedback_cell

放心,它不会造成内存泄漏。它是v8对频繁运行的热代码做出的优化,会被v8自己回收。详见这篇文章:Feedback vectors in heap snapshots – Rohit Pagariya

常见的内存泄漏场景有哪些?

这里列举了一些常见的内存泄漏场景,遇到内存泄漏问题时可以先自查一遍常见场景,个人感觉能解决日常开发中遇到的90%内存泄漏

  • console导致的内存泄漏 因为打印后的对象需要支持在控制台上查看,所以传递给console.log方法的对象是不能被垃圾回收的。我们需要避免在生产环境用console打印对象。

  • 框架配合第三方库使用时,没有及时执行销毁 这点可以参考vue cookbook里的例子

  • 被遗忘的定时器 例如在组件初始化的时候设置了setInterval,那么在组件销毁之前记得调用clearInterval方法取消定时器。

  • 没有正确移除事件监听器(各种EventBus, dom事件监听等) 这应该是最容易犯的一个错误,无论新手老手都有可能栽在这里。
    特征:performance里,监听器数量会持续上升

怎么排查Javascript内存泄漏

持续上升的监听器数量

啰嗦一句:尽管大部分同学都会有主动移除监听器的观念,但如果姿势不对,可能依旧会造成内存泄漏。下面是一个真实案例:

// 版本一mounted() {    window.addEventListener('resize', debounce(this.handleWidthChange, 100))},beforeDestroy() {    window.removeEventListener('resize', debounce(this.handleWidthChange, 100)) }

乍一看好像写的还不错,有及时移除监听器,对resize这种频繁触发的事件也加了debounce处理。但其实这段代码就导致了内存泄漏:每次调用debounce(this.handleWidthChange, 100)时, 其实都会返回一个新的函数,导致addEventListener和 removeEventListener方法传入的回调函数已经不是同一个回调函数,监听器没有被正确移除,内存泄漏。

下面来看修改后的代码:

// 版本二data() {    return {        debounceWidthChange: null    }},mounted() {    this.debounceWidthChange = debounce(this.handleWidthChange, 100)    window.addEventListener('resize', this.debounceWidthChange)},beforeDestroyed() {    window.removeEventListener('resize', this.debounceWidthChange)  }

修改后,监听和移除监听的已经是同一个回调函数了,看起来似乎已经没问题。然而,这段代码还是有内存泄漏的问题。没看出问题的小伙伴可以对比一下正确答案:

// 版本三data() {    return {        debounceWidthChange: null    }},mounted() {    this.debounceWidthChange = debounce(this.handleWidthChange, 100)    window.addEventListener('resize', this.debounceWidthChange)},beforeDestroy() {    window.removeEventListener('resize', this.debounceWidthChange)  }

是的,答案非常狗血:Vue只有destroyedbeforeDestroy这两个生命周期,没有 beforeDestroyed,所以上面的beforeDestroyed函数永远不会执行,导致了内存泄漏。

感谢各位的阅读,以上就是“怎么排查Javascript内存泄漏”的内容了,经过本文的学习后,相信大家对怎么排查Javascript内存泄漏这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

怎么排查Javascript内存泄漏

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

怎么排查Javascript内存泄漏

这篇文章主要讲解了“怎么排查Javascript内存泄漏”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么排查Javascript内存泄漏”吧!如何判断我的应用发生了内存泄漏为了证明螃蟹的听
2023-07-02

linux内存泄漏怎么排查

要排查Linux中的内存泄漏,可以采取以下步骤:监视和跟踪内存使用:使用工具如top、htop、vmstat等监视系统的内存使用情况。注意查看可用内存、缓存、交换空间等指标,以确定是否存在内存泄漏的迹象。分析进程内存使用情况:使用工具如ps
linux内存泄漏怎么排查
2024-02-29

java内存泄漏怎么排查

Java内存泄漏是指在程序运行过程中,不再使用的对象仍然占用着内存空间,导致内存无法被回收。以下是一些常见的排查内存泄漏的方法:1. 使用内存分析工具:可以使用一些专业的内存分析工具,如Eclipse Memory Analyzer(MAT
2023-08-31

golang内存泄漏怎么排查

在 Go 语言中,内存泄漏通常是由于不正确地使用或管理指针和引用导致的。以下是一些排查内存泄漏的常用方法:1. 使用 go build -race 或 go run -race 命令编译和运行程序,启用竞争检查器(race detector
2023-10-21

java堆外内存泄漏怎么排查

在Java中,堆外内存通常是通过直接内存(Direct Memory)分配的。直接内存是一种不受Java堆内存管理的内存分配方式,它是通过调用系统的本地方法进行分配和释放的。当出现堆外内存泄漏时,可以使用以下方法进行排查:检查代码中直接内
2023-10-27

linux内存泄漏问题怎么排查

要排查Linux中的内存泄漏问题,可以按照以下步骤进行:1. 监控内存使用情况:使用工具如top、free或htop等监控系统的实时内存使用情况,观察内存占用是否逐渐增加,并查看哪个进程占用了大量内存。2. 检查系统日志:查看系统日志文件(
2023-10-21

Node如何排查内存泄漏

这篇文章主要讲解了“Node如何排查内存泄漏”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Node如何排查内存泄漏”吧!在 Nodejs 服务端开发的场景中,内存泄漏 绝对是最令人头疼的问题
2023-07-05

jvm内存泄漏排查流程

1.查询cpu消耗最大的进程jps 先找出来那些java进程top 命令查看那些java进程消耗的cpu比较大2.查找占用内存最大的线程1.命令:ps p pid -L -o pcpu,pid,tid,time,tname,cmd%CPU PID TI
jvm内存泄漏排查流程
2019-02-04

Android内存泄漏排查利器LeakCanary

本文为大家分享了Android内存泄漏排查利器,供大家参考,具体内容如下 开源地址:https://github.com/square/leakcanary 在 build.gralde 里加上依赖, 然后sync 一下, 添加内容如下de
2022-06-06

Java内存泄漏的排查分析

本篇内容介绍了“Java内存泄漏的排查分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、由来前些日子小组内安排值班,轮流看顾我们的服务,
2023-06-02

怎么避免JavaScript内存泄漏

这篇文章主要介绍“怎么避免JavaScript内存泄漏”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“怎么避免JavaScript内存泄漏”文章能帮助大家解决问题。一、什么是内存泄漏JavaScrip
2023-06-30

java堆外内存泄漏如何排查

在Java中,堆外内存泄漏通常是由于未正确释放使用了堆外内存的资源所导致的。下面是一些排查堆外内存泄漏的常见方法:1. 使用JVM工具进行监测:可以使用JVM提供的命令行工具如jmap、jstat等来监测堆外内存的使用情况,例如使用jmap
2023-08-24

java怎么检查内存泄漏

本篇内容介绍了“java怎么检查内存泄漏”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!内存泄漏场景长生命周期的对象持有短生命周期对象的引用就
2023-06-30

GoLang内存泄漏原因排查详解

内存溢出是指程序在申请内存时,没有足够的内存空间供其使用,简单点说就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出出现outofmemory异常
2022-12-15

Java内存泄漏实例排查分析

这篇文章主要介绍“Java内存泄漏实例排查分析”,在日常操作中,相信很多人在Java内存泄漏实例排查分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java内存泄漏实例排查分析”的疑惑有所帮助!接下来,请跟
2023-06-16

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录