JavaScript 内存泄漏

内存相关问题

  • 内存泄漏:当不再用到的对象内存,没有及时被回收,想要避免就让无用数据不存在引用关系。
  • 内存膨胀:即在短时间内内存占用极速上升到达一个峰值,想要避免需要使用技术手段减少对内存的占用。
  • 频繁GC: GC执行的特别频繁,一般出现在频繁使用大的临时变量导致新生代空间被装满的速度极快,而每次新生代装满时就会触发 GC,频繁 GC 同样会导致页面卡顿,想要避免的话就不要搞太多的临时变量,因为临时变量不用了就会被回收。

内存泄露场景

闭包

  • 有权访问另一个函数作用域中的变量的函数

隐式全局变量

  • 函数中的局部变量在函数执行结束后这些变量已经不再被需要,所以垃圾回收器会识别并释放它们。但是对于全局变量,垃圾回收器很难判断这些变量什么时候才不被需要,所以全局变量通常不会被回收
function fn(){
  // 没有声明从而制造了隐式全局变量test1
  test1 = new Array(1000).fill('isboyjc1')
  
  // 函数内部this指向window,制造了隐式全局变量test2
  this.test2 = new Array(1000).fill('isboyjc2')
}
fn()

  • 因为没有声明和函数中this的问题造成了两个额外的隐式全局变量,这两个变量不会被回收

DOM引用

<div id="root">
  <ul id="ul">
    <li></li>
    <li></li>
    <li id="li3"></li>
    <li></li>
  </ul>
</div>
<script>
  let root = document.querySelector('#root')
  let ul = document.querySelector('#ul')
  let li3 = document.querySelector('#li3')
  
  // 由于ul变量存在,整个ul及其子元素都不能GC
  root.removeChild(ul)
  
  // 虽置空了ul变量,但由于li3变量引用ul的子节点,所以ul元素依然不能被GC
  ul = null
  
  // 已无变量引用,此时可以GC
  li3 = null
</script>


在这里插入图片描述

定时器

  • 当不需要 interval 或者 timeout 时,最好调用 clearInterval 或者 clearTimeout来清除,另外,浏览器中的 requestAnimationFrame 也存在这个问题,我们需要在不需要的时候用 cancelAnimationFrame API 来取消使用

事件监听

<template>
  <div></div>
</template>

<script>
export default {
  created() {
    window.addEventListener("resize", this.doSomething)
  },
  beforeDestroy(){
    window.removeEventListener("resize", this.doSomething)
  },
  methods: {
    doSomething() {
      // do something
    }
  }
}
</script>

事件的监听发布

<template>
  <div></div>
</template>

<script>
export default {
  created() {
    eventBus.on("test", this.doSomething)
  },
  beforeDestroy(){
    eventBus.off("test", this.doSomething)
  },
  methods: {
    doSomething() {
      // do something
    }
  }
}
</script>

  • 当实现了监听者模式并收集了相关的事件处理函数,其中引用的变量或者函数都被认为是需要的而不会进行回收

Map、Set强引用

let obj = {id: 1}
let user = {info: obj}
let set = new Set([obj])
let map = new Map([[obj, 'hahaha']])

// 重写obj
obj = null 

//以下强引用,并不会因为垃圾回收掉
console.log(user.info) // {id: 1}
console.log(set)
console.log(map)

map.delete(obj);		
obj = null;	//此时才能去除map对key所引用对象的强引用


let weakSet = new WeakSet([obj])
let weakMap = new WeakMap([[obj, 'hahaha']])

// 重写obj引用
obj = null
// {id: 1} 将在下一次 GC 中从内存中删除

未清理的console

  • 之所以在控制台能看到数据输出,是因为浏览器保存了我们输出对象的信息数据引用,也正是因此未清理的 console 如果输出了对象也会造成内存泄漏

chrome 内存泄漏定位步骤

在这里插入图片描述

  • 先点击6手动进行一次垃圾回收,4、5都点上,然后点击1开始录制
    在这里插入图片描述
  • 可以看到快照对应下的内存曲线,如果在手动执行垃圾回收后,曲线并没有明显下降,说明可能存在内存泄漏
  • 点击Memory,可以为我们提供更多详细信息,比如记录 JS CPU 执行时间细节、显示 JS 对象和相关的DOM节点的内存消耗、记录内存的分配细节等
  • 其中的 Heap Profiling 可以记录当前的堆内存 heap 的快照,并生成对象的描述文件,该描述文件给出了当下 JS 运行所用的所有对象,以及这些对象所占用的内存大小、引用的层级关系等等,用它就可以定位出引起问题的具体原因以及位置。
    在这里插入图片描述
  • 先手动点击垃圾回收,然后操作页面,然后点击1生成快照,循环操作几次
    在这里插入图片描述
Summary:按照构造函数进行分组,捕获对象和其使用内存的情况,可理解为一个内存摘要,用于跟踪定位DOM节点的内存泄漏
Comparison:对比某个操作前后的内存快照区别,分析操作前后内存释放情况等,便于确认内存是否存在泄漏及造成原因
Containment:探测堆的具体内容,提供一个视图来查看对象结构,有助分析对象引用情况,可分析闭包及更深层次的对象分析
Statistics:统计视图

在这里插入图片描述

Constructor:显示所有的构造函数,点击每一个构造函数可以查看由该构造函数创建的所有对象
Distance:显示通过最短的节点路径到根节点的距离,引用层级
Shallow Size:显示对象所占内存,不包含内部引用的其他对象所占的内存
Retained Size:显示对象所占的总内存,包含内部引用的其他对象所占的内存

//Constructor这一列
system、system/Context 表示引擎自己创建的以及上下文创建的一些引用,这些不用太关注,不重要
closure 表示一些函数闭包中的对象引用
array、string、number、regexp 这一系列也能看出,就是引用了数组、字符串、数字或正则表达式的对象类型
HTMLDivElement、HTMLAnchorElement、DocumentFragment等等这些其实就是你的代码中对元素的引用或者指定的 DOM 对象引用
  • 选择Comparison比较,或者右侧的filter
    在这里插入图片描述在这里插入图片描述

New:新建了多少个对象
Deleted:回收了多少个对象
Delta:新建的对象数 减去 回收的对象数

  • 需要重点关注 Delta ,只要它是正数就可能存在问题

在这里插入图片描述

  • closure 上面也说过代表闭包引用,可以查看到代码中发生引用的位置
  • 根据其他的如Array等构造函数的Distance层级也能定位发生泄漏的位置
  • 如上就能定位两个问题,一是代码 30 行的闭包引用数组造成的内存泄漏,二是数组的元素不断增多造成的内存泄漏。

内存泄漏检测

FinalizationRegistry

  • 给对象注册一个在被垃圾回收时触发的回调,排除掉被回收调的对象

详情

第三方工具

  • node-heapdump:heapdump是一个dumpV8堆信息的工具
  • node-profiler:node-profiler 是 alinode 团队出品的一个 与node-heapdump 类似的抓取内存堆快照的工具
  • Easy-Monitor:轻量级的 Node.js 项目内核性能监控 + 分析工具
  • Node.js-Troubleshooting-Guide:Node.js 应用线上/线下故障、压测问题和性能调优指南手册
  • alinode:Node.js 性能平台(Node.js Performance Platform)是面向中大型 Node.js 应用提供 性能监控、安全提醒、故障排查、性能优化等服务的整体性解决方案。
  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值