如何避免JavaScript中的内存泄漏?

前言

过去,我们浏览静态网站时无须过多关注内存管理,因为加载新页面时,之前的页面信息会从内存中删除。 然而,随着单页Web应用(SPA)的兴起,应用程序消耗的内存越来越多,这不仅会降低浏览器性能,甚至会导致浏览器卡死。因此,在编码实践中,开发人员需要更加关注与内存相关的内容。因此,小编今天将为大家介绍JavaScript内存泄漏的编程模式,并提供一些内存管理的改进方法。

什么是内存泄漏以及如何发现它?

如何避免JavaScript中的内存泄漏?_缓存

什么是内存泄漏?

JavaScript对象被保存在浏览器内存的堆中,并通过引用方式访问。值得一提的是,JavaScript垃圾回收器则运行于后台,并通过识别无法访问的对象来释放并恢复底层存储空间,从而保证JavaScript引擎的良好运行状态。

当内存中的对象在垃圾回收周期中应该被清理时,若它们被另一个仍然存在于内存中的对象通过一个意外的引用所持有,就会引发内存泄漏问题。这种情况下,冗余对象会继续占据内存空间,导致应用程序消耗过多的内存资源,并可能导致性能下降和表现不佳的情况出现。因此,及时清理无用对象并释放内存资源是至关重要的,以确保应用程序的正常运行和良好的性能表现。

如何发现内存泄漏?

那么如何知道代码中是否存在内存泄漏?内存泄漏往往隐蔽且很难检测和定位。即使代码中存在内存泄漏,浏览器在运行时也不会返回任何错误。如果注意到页面的性能逐渐下降,可以使用浏览器内置的工具来确定是否存在内存泄漏以及是哪个对象引起的。

任务管理器(不要与操作系统的任务管理器混淆)提供了浏览器中所有选项卡和进程的概览。Chrome 中,可以通过在 Linux 和 Windows 操作系统上按 Shift+Esc 来打开任务管理器;而在 Firefox 中,通过在地址栏中键入 about:performance 则可以访问内置的管理器,它可以显示每个标签的 JavaScript 内存占用情况。如果网站停留在那里什么都不做,但 JavaScript内存使用量逐渐增加,那很可能是存在内存泄漏。

如何避免JavaScript中的内存泄漏?_缓存_02

开发者工具提供了一些先进的内存管理方法,例如,使用Chrome浏览器的性能记录工具,可以对页面的性能进行可视化分析。在这个过程中,可以通过一些指标来判断是否存在内存泄漏问题,比如堆内存使用量增加的情况,并及时采取措施解决这些问题,以确保应用程序的正常运行和良好的性能表现。

如何避免JavaScript中的内存泄漏?_应用程序_03

另外,通过Chrome和Firefox的开发者工具提供的内存工具,可以进一步探索内存使用情况。队列内存使用快照的比较可以显示在两个快照之间分配了多少内存以及分配的位置,并提供额外信息来帮助识别代码中存在问题的对象。这些工具为开发者提供了便利,能够更好地进行内存管理和性能优化,提高应用程序的质量和性能。

JavaScript代码中常见的内存泄漏的常见来源:

研究内存泄漏问题就相当于寻找符合垃圾回收机制的编程方式,有效避免对象引用的问题。下面小编就为大家介绍几个常见的容易导致内存泄漏的地方:

1.全局变量

全局变量始终存储在根目录下,且永远不会被回收。而在JavaScript的开发中,一些错误会导致局部变量被转换到了全局,尤其是在非严格的代码模式下。下面是两个常见的局部变量被转化到全局变量的情况:

  1. 为未声明的变量赋值
  2. 使用this指向全局对象。

**注意:**严格模式(“use strict”)将帮助您避免上面示例中的内存泄漏和控制台错误。

2.闭包

函数中定义的变量会在函数退出调用栈并且在函数外部没有指向它的引用时被清除。而闭包则会保持被引用的变量一直存在,即便函数的执行已经终止。

在这个例子中,potentiallyHugeArray从未被任何函数返回,也无法被访问,但它的大小会随着调用 inner 方法的次数而增长。

3.定时器

在JavaScript中,使用使用 setTimeout 或 setInterval函数引用对象是防止对象被垃圾回收的最常见方法。当在代码中设置循环定时器(可以使 setTimeout 表现得像 setInterval,即使其递归)时,只要回调可调用,定时器回调对象的引用就会永远保持活动状态。

例如下面的这段代码,只有在移除定时器后,data对象才会被垃圾回收。在没有移除setInterval之前,它永远不会被删除,并且data.hugeString 会一直保留在内存中,直到应用程序停止。

那么应该如何避免上述这种情况的发生呢?可以从以下两个方法入手:

  1. 注意定时器回调引用的对象。
  2. 必要时取消定时器。

如下方的代码所示:

4.事件监听

活动的事件监听器会阻止其范围内的所有变量被回收。一旦添加,事件监听器会一直生效,直到下面两种情况的发生:

  1. 通过 removeEventListener() 移除。
  2. 相关联的 DOM 元素被移除。

在下面的示例中,使用匿名内联函数作为事件监听器,这意味着它不能与 removeEventListener() 一起使用。此外,由于document 不能被移除,触发方法中的内容会一直驻留内存,即使只使用它触发一次。

那么如何避免这种情况呢?可以通过removeEventListener()释放监听器:

如果事件监听器只需要运行一次,addEventListener() 可以带有第三个参数,一个提供附加选项的对象。只要将 {once: true} 作为第三个参数传递给 addEventListener(),监听器将在事件处理一次后自动删除。

5.缓存

如果不断向缓存中添加内容,而未使用的对象也没有移除,也没有限制缓存的大小,那么缓存的大小就会无限增长:

为了解决这个问题,需要清除不需要的缓存:

一种有效的解决内存泄漏问题的方法是使用WeakMap。它是一种数据结构,其中键引用被保持为弱引用,并且仅接受对象作为键。如果使用对象作为键,并且它是唯一引用该对象的引用,相关条目将从缓存中移除,并进行垃圾回收。在下面的示例中,当替换user_1后,与之关联的条目将在下一次垃圾回收时自动从WeakMap中移除。

结论

对于复杂的应用程序,检测和修复 JavaScript 内存泄漏问题可能是一项非常艰巨的任务。了解内存泄漏的常见原因以防止它们发生是非常重要的。在涉及内存和性能方面,最重要的是用户体验,这才是最重要的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/164948.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 学习(CentOS 7)

CentOS 7 学习 Linux系统内核作者: Linux内核版本 内核(kernel)是系统的心脏,是运行程序和管理像磁盘和打印机等硬件设备的核心程序,它提供了一个在裸设备与应用程序间的抽象层。 Linux内核版本又分为稳定版和开发版,两种版本是相互关联&am…

【广州华锐互动】影视制作VR在线学习:身临其境,提高学习效率

随着科技的不断发展,影视后期制作技术也在日新月异。然而,传统的教学方式往往难以满足学员的学习需求,无法充分展现影视后期制作的魅力和潜力。近年来,虚拟现实(VR)技术的崛起为教学领域带来了新的机遇。通过VR教学课件&#xff0…

iview table 表格合并单元格

一、如图所示 二、实现方式 表格用提供的span-method属性 <template><Table ref"table" border :span-method"handleSpan" :row-key"true" :columns"tableColumns" :data"tableData"no-data-text"暂无数据&…

北斗卫星助力火车站形变监测,确保安全稳定运行

北斗卫星助力火车站形变监测&#xff0c;确保安全稳定运行 去年6月&#xff0c;京广高铁武广段成功实现了时速350公里的运营&#xff0c;这标志着亚洲最大铁路枢纽客站——北京丰台站正式开通运营。值得一提的是&#xff0c;北京丰台站在安全监测方面&#xff0c;已经将“北斗智…

【计算机网络】物理层知识

目录 1、物理层的基本概念 2、数据通信的基础知识 2.1、数据通信系统模型 2.2、信道的几个基本概念 3、物理层下面的传输媒体 4、信道复用技术 1、物理层的基本概念 物理层考虑的是怎样才能在连接各种计算机的传输媒体上传输数据比特流&#xff0c;而不是指具体的 传输媒…

【Redis】set常用命令集合间操作内部编码使用场景

文章目录 前置知识常见命令SADDSMEMBERSSISMEMBERSCARDSPOPSMOVESREM 集合间操作SINTERSINTERSTORESUNIONSUNIONSTORESDIFFSDIFFSTORE 命令小结内部编码测试内部编码 使用场景 前置知识 集合类型也是保存多个字符串类型的元素的&#xff0c;但和列表类型不同的是&#xff0c;在…

react-app-env.d.ts是什么?

react-app-env.d.ts这个文件是使用CRA脚手架生成react项目时自动生成的&#xff0c;在平时的开发过程中看到这个文件就会感觉很疑惑&#xff0c;出于好奇心&#xff0c;在网上查找资料&#xff0c;得出下文 前置知识 这个是一个类型声明文件 它的内容很短&#xff0c;就一行…

Nodejs的安装以及配置(node-v12.16.1-x64.msi)

Nodejs的安装以及配置 1、安装 node-v12.16.1-x64.msi点击安装&#xff0c;注意以下步骤 本文设置nodejs的安装的路径&#xff1a;D:\soft\nodejs 继续点击next&#xff0c;选中Add to PATH &#xff0c;旁边的英文告诉我们会把 环境变量 给我们配置好 当然也可以只选择 Nod…

好消息,微信消费者投诉工具升级,可以直接回复用户、处理投诉了。。。

大家好&#xff0c;我是小悟 兄弟们&#xff0c;阅读本文之前&#xff0c;建议先阅读【连夜干出来一个自动处理【微信消费者投诉管理系统】&#xff0c;支持多商户】。 为了使工具更好用&#xff0c;也为帮助商户更好地处理消费者投诉&#xff0c;提升用户满意度&#xff0c;…

08 # 手写 filter 方法

什么是 filter filter() 方法创建给定数组一部分的浅拷贝&#xff0c;其包含通过所提供函数实现的测试的所有元素。如果没有元素通过测试&#xff0c;则返回一个空数组。 ele&#xff1a;表示数组中的每一个元素index&#xff1a;表示数据中元素的索引array&#xff1a;表示数…

光纤的跳线和尾纤区别

光纤跳线和光纤尾纤在结构上、连接方式、应用场景等方面存在明显的区别。 光纤跳线有0.9、2.0、3.0&#xff0c;通常是区分光缆外径的。0.9光缆外径0.9mm的&#xff0c;2.0光缆外径2mm&#xff0c;3.0光缆外径3mm。 同时分单模光纤跳线和多模光纤跳线。单模一般是黄色&#xff…

代码随想录算法训练营第四十二天丨 动态规划part05

1049.最后一块石头的重量II 思路 本题其实就是尽量让石头分成重量相同的两堆&#xff0c;相撞之后剩下的石头最小&#xff0c;这样就化解成01背包问题了。 感觉和昨天讲解的416. 分割等和子集 (opens new window)非常像了。 本题物品的重量为 stones[i]&#xff0c;物品的价…