前端内存泄漏和溢出的情况以及解决办法

news2024/11/18 3:23:26

写在前面:

在平时写代码时,内存泄漏的情况会时有发生,虽然js有内存回收机制,但在平时编程中还是需要注意避免内存泄漏的情况;前几天做移动端时遇到一个内存泄漏造成移动端页面卡顿的问题,所以想总结下前端内存泄漏的情况,回顾下基础知识

一、什么是内存泄漏

 程序运行时操作系统会分配相应的内存,如果不进行定时的清理内存的占用情况,内存占用越来越高,很容易造成页面卡顿,进程奔溃;如果程序在系统分配了内存空间后不再使用但是没有及时释放就会造成内存泄漏;程序向系统申请的内存空间超出了系统能给的,就造成了内存溢出。内存泄漏和溢出都会影响程序的性能。

js不需要手动给变量申请内存,当我们在申明一个变量时,js会自动为其分配内存;当某个对象没有被引用会进行回收,最简单的垃圾回收机制是引用计数,当某个对象被引用的次数达到0时就会被回收

二、常见的造成内存泄漏的情况

1.全局变量:一个变量被挂载到window上,那么它永远都是可达的,只有关闭页面或关闭浏览器时被回收。严格模式下可以避免这个情况;解决方法:testVal = null

局部变量:在函数执行过后,局部变量就被回收了,此时便可以将它引用的内存释放掉

另一种全局变量可能由this创建

function foo() {
    this.variable = "potential accidental global";
}
// foo 调用自己,此时this 指向了全局对象(window)
foo();

2.定时器

使用定时器时,我们销毁了这个DOM,但是在定时器中使用了这个DOM,定时器中就保留了对这个DOM的引用,所以需要在清除DOM时也要手动清除定时器(timer = null)

var someResource = getData();
const timer = setInterval(function() {
    var node = document.getElementById('Node');
    if(node) {
        // 处理 node 和 someResource
        node.innerHTML = JSON.stringify(someResource));
    }
}, 1000);

解决:document.body.removeChild(node);someResource = null

timer = null ,这样定时器中的node 和someResource才会真的被回收掉

3.闭包函数导致的泄漏

闭包可以维持函数内部的局部变量,使其变量得不到释放

function bindEvent() {
  var obj = document.createElement('XXX');
  var unused = function () {
    console.log(obj, '闭包内引用obj obj不会被释放');
  };
  obj = null; // 解决方法
}

4.DOM元素的事件监听

btn.addEventListener('click', onClick)
btn.removeEventListener('click', onClick)

5.没有清理对DOM元素的引用

const refA = document.getElementById('refA');
document.body.removeChild(refA); // dom删除了
console.log(refA, 'refA'); // 但是还存在引用,能console出整个div 没有被回收
refA = null;  // 这里会报错  Assignment to constant variable(赋值给常数变量).因为这个refA是const声明的,值不可以改变,将const改为var
console.log(refA, 'refA'); // 解除引用

三、Vue中占用内存的几种情况

1.全局变量在切换页面时没有清空

mounted() {
      window.test = {
        // 此处在全局window对象中引用了本页面的dom对象
        name: 'home',
        node: document.getElementById('home'),
      }
    },

解决:在页面卸载时顺便清空该引用

destroyed(){

  window.test = null

}

2.监听在window或body上的事件没有解绑

  • 如果在mounted/created 钩子中绑定了DOM/BOM 对象中的事件,需要在beforeDestroy 中做对应解绑处理
  • 如果在mounted/created 钩子中使用了第三方库初始化,需要在beforeDestroy 中做对应销毁处理
  • 如果组件中使用了定时器,需要在beforeDestroy 中做对应销毁处理
  • 某些组件在模板中使用 事件绑定可能会出现泄漏,使用$on 替换模板中的绑定

如果是要render函数,避免在html标签中DOM / BOM事件

mounted () {
  window.addEventListener('resize', this.func)
},
methods:{
    func(){}
}

// 解决:
beforeDestroy () {
  window.removeEventListener('resize', this.func)
}

3.在进入页面时打开弹框dialog,在离开页面时没有设为false

mounted () {
  this.Dialog = true
},

// 在手动点击头部的返回按钮和回到首页的按钮时,会造成页面卡顿,无法滑动的问题

// 在离开页面时将它设为false
  beforeRouteLeave(to, from, next){
    this.Dialog = false
    next()
  },

 4.绑在EventBus的事件没有解绑

解决:页面卸载时解除引用

mounted () {
 this.$EventBus.$on('homeTask', res => this.func(res))
},
destroyed () {
 this.$EventBus.$off()
}

5.v-if指令产生的内存泄漏

在使用v-if来控制显示隐藏的时候,当v-if的条件为false时,浏览器不会渲染这个元素;但是当我们在v-if为true时,给这个元素内添加了其他子元素,那么我们在设为false的时候只是这个父元素本身从虚拟DOM中移除它,新添加的子元素并没有移除,子元素对父元素引用了,父元素也就不会被移除

例子:

<div id="app">
  <button v-if="showChoices" @click="hide">Hide</button>
  <button v-if="!showChoices" @click="show">Show</button>
  <div v-if="showChoices">
    <select id="choices-single-default"></select>
  </div>

原来的写法

<script>
  export default {
    data() {
      return {
        showChoices: true,
      }
    },
    mounted: function () {
      this.initializeChoices()
    },
    methods: {
      initializeChoices: function () {
        let list = []
        // 我们来为选择框载入很多选项,这样的话它会占用大量的内存
        for (let i = 0; i < 1000; i++) {
          list.push({
            label: 'Item ' + i,
            value: i,
          })
        }
        new Choices('#choices-single-default', {
          searchEnabled: true,
          removeItemButton: true,
          choices: list,
        })
      },
      show: function () {
        this.showChoices = true
        this.$nextTick(() => {
          this.initializeChoices()
        })
      },
      hide: function () {
        this.showChoices = false
      },
    },
  }
</script>

处理后的写法:我们在hide方法里移除父元素时对子元素做一个清理

<div id="app">
  <button v-if="showChoices" @click="hide">Hide</button>
  <button v-if="!showChoices" @click="show">Show</button>
  <div v-if="showChoices">
    <select id="choices-single-default"></select>
  </div>
</div>
 
<script>
  export default {
    data() {
      return {
        showChoices: true,
        choicesSelect: null
      }
    },
    mounted: function () {
      this.initializeChoices()
    },
    methods: {
      initializeChoices: function () {
        let list = []
        for (let i = 0; i < 1000; i++) {
          list.push({
            label: 'Item ' + i,
            value: i,
          })
        }
         // 在我们的 Vue 实例的数据对象中设置一个 `choicesSelect` 的引用
        this.choicesSelect = new Choices("#choices-single-default", {
          searchEnabled: true,
          removeItemButton: true,
          choices: list,
        })
      },
      show: function () {
        this.showChoices = true
        this.$nextTick(() => {
          this.initializeChoices()
        })
      },
      hide: function () {
        // 现在我们可以让 Choices 使用这个引用,从 DOM 中移除这些元素之前进行清理工作
        this.choicesSelect.destroy()
        this.showChoices = false
      },
    },
  }
</script>

 6.echart图表带来的内存泄漏

每一个图例在没有数据的时候它会创建一个定时器去渲染气泡,页面切换后,echarts 图例是销毁了,但是这个 echarts 的实例还在内存当中,同时它的气泡渲染定时器还在运行。这就导致 Echarts 占用 CPU 高,导致浏览器卡顿,当数据量比较大时甚至浏览器崩溃。

解决方法:加一个 beforeDestroy()方法释放该页面的 chart 资源,我也试过使用 dispose()方法,但是 dispose 销毁这个图例,图例是不存在了,但图例的 resize()方法会启动,则会报没有 resize 这个方法,而 clear()方法则是清空图例数据,不影响图例的 resize,而且能够释放内存,切换的时候就很顺畅了。

beforeDestroy () {
  this.chart.clear()
}

ES6中防止内存泄漏

在平时编程中我们可能没有及时的清除引用,ES6中新增了两种数据结构:weakset 和 weakmap,他们对值得引用时不计入垃圾回收机制的,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存。

const wm = new WeakMap()
const element = document.getElementById('example')
vm.set(element, 'something')
vm.get(element)

上面代码中,先新建一个 Weakmap 实例。然后,将一个 DOM 节点作为键名存入该实例,并将一些附加信息作为键值,一起存放在 WeakMap 里面。这时,WeakMap 里面对 element 的引用就是弱引用,不会被计入垃圾回收机制。

注册监听事件的 listener 对象很适合用 WeakMap 来实现

<div id="example">点击</div>

// 代码1
element.addEventListener('click', handler, false)
 
// 代码2
const listener = new WeakMap()
listener.set(element, handler)
element.addEventListener('click', listener.get(element), false)

function handler(){
    console.log('测试')
}

好处是:由于监听函数是放在 WeakMap 里面,一旦 dom 对象 ele 消失,与它绑定的监听函数 handler 也会自动消失。

以上总结有些是平时工作中遇到的需要注意的点,有些是借鉴别人的blog:

前端常见内存泄漏及解决方案_傲娇的koala的博客-CSDN博客

 https://mp.weixin.qq.com/s/TEs6JKQsRo2ZbVhVfAuOBA

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GO语言网络编程(并发编程)Channel

GO语言网络编程&#xff08;并发编程&#xff09;Channel 1、Channel 1.1.1 Channel 单纯地将函数并发执行是没有意义的。函数与函数间需要交换数据才能体现并发执行函数的意义。 虽然可以使用共享内存进行数据交换&#xff0c;但是共享内存在不同的goroutine中容易发生竞态…

Linux Debian12使用git将本地项目打标签、创建分支和分支合并到master再上传到码云(gitee)远程仓库

一、git创建分支并克隆指定分支到本地 gitee官网&#xff1a;https://gitee.com/ 登录上gitee账号,我这用test仓库作测试。新建分支名称为develop&#xff0c;分支起点选择master&#xff0c;创建即可&#xff0c;如下图所示&#xff1a; 使用git管理代码版本时&#xff0…

SpringBoot 中的事务管理讲解

Spring Boot 中的事务管理 在实际的开发中&#xff0c;事务是非常重要的一个概念。在 Spring Boot 中&#xff0c;我们可以使用事务管理器来管理事务。事务管理器可以确保一系列操作要么全部成功&#xff0c;要么全部失败&#xff0c;从而保证数据的一致性和完整性。在本文中&…

音频驱动嘴型的视频数字人虚拟主播工具motionface replay使用教程

音频驱动嘴型的视频数字人虚拟主播工具motionface replay使用教程 1&#xff1a;系统要求 软件运行支持32位/64位window 10/11系统&#xff0c;内存最低要求> 8Gb.无其他硬性要求。 1&#xff1a;下载安装 打开百度网盘链接下载&#xff1a; 链接&#xff1a;百度网盘 请输入…

unityhub登录不上,登录界面刷新不出来, 或检测不到Unity

如果是登录界面白屏的话 或 检测不到Unity 可以尝试 1.关闭hub&#xff08;右下角也要退出&#xff0c;找不到的可以直接任务管理器退出&#xff09; 2.删除 %UserProfile%\AppData\Roaming 里面的 \UnityHub\Cache和\Unity\Caches 文件夹 C:\Users\Administrator\AppData\Ro…

Linkstech多核并行仿真丨光伏发电系统模型及IEEE 39 bus模型多核并行实测

新能源场站和区域电网作为复杂且具有动态特性的大规模电力系统&#xff0c;需要实时仿真测试来验证其性能、稳定性和响应能力。在这种背景下&#xff0c;多核并行仿真运算显得尤为重要。多核并行仿真能够同时处理电力系统的复杂模型&#xff0c;加速仿真过程&#xff0c;实现接…

C#自定义控件组件实现Chart图表(多Y轴,选择图例加粗,选择放大,缩放,点击查看信息等功能)

先看看ECharts的效果 C# 工具箱里的Chart控件就不演示了,很多效果没办法做出来,做出来效果也很不理想。所以,需要自己去手动实现工具箱里的Chart没办法实现的效果; 先看看实现后的效果 绑定数据 点击图表 点击右侧图例加粗 选择放大 右键 点击缩小,恢复

Docker Desktop使用宿主机Windows的配置文件持久化存储数据

《微软官网Windows 上的 Docker 引擎帮助文档》 以Nginx为例 拉取Nginx镜像 docker pull nginx:1.23.1先启动Nginx&#xff0c;然后把Nginx的配置文件复制到Windows docker run --namenginx-1.23.1 -d -p 80:80 nginx:1.23.1把Nginx的配置文件复制到Windows 注意&#xff1…

【算法训练-数组 四】【合并】:合并两个有序数组

废话不多说&#xff0c;喊一句号子鼓励自己&#xff1a;程序员永不失业&#xff0c;程序员走向架构&#xff01;本篇Blog的主题是【数组合并】&#xff0c;使用【数组】这个基本的数据结构来实现&#xff0c;这个高频题的站点是&#xff1a;CodeTop&#xff0c;筛选条件为&…

【深度学习】分类损失函数解析

【深度学习】分类相关的损失解析 文章目录 【深度学习】分类相关的损失解析1. 介绍2. 解析3. 代码示例 1. 介绍 在分类任务中&#xff0c;我们通常使用各种损失函数来衡量模型输出与真实标签之间的差异。有时候搞不清楚用什么&#xff0c;下面是几种常见的分类相关损失函数及其…

计算机网络TCP篇之流量控制

计算机网络TCP篇之流量控制 今天谈一谈我对于tcp流量控制的看法 在网络拓扑中如果发送方节点的发送速率大于接受方节点的接受速率&#xff0c;数据会不断在接受方的缓冲区累积&#xff0c;直到接受方的缓冲区满的时候&#xff0c;发送方继续发送数据&#xff0c;这时候接受方无…

redisson使用过程常见问题汇总

文章目录 常见报错1. 配置方式使用错误2. 版本差异报错3. 配置文件中配置了密码或者配置错误4. 字符集和序列化方式配置问题5. Redisson的序列化问题6. 连接池问题&#xff1a;7. Redisson的高可用性问题&#xff1a;8. Redisson的并发问题9. Redisson的性能问题 2. 参考文档 常…

springboot第36集:kafka,JSAPI支付,nginx,微服务Feign与flutter安卓App开发2

去思考自己的项目有哪些让你觉得不好的地方&#xff0c;然后去解决它&#xff0c;而非学习了一堆原理&#xff0c;展示技术能力比展示技术知识更有说服力 其实边解决问题边记录&#xff0c;是一个好习惯&#xff0c;不仅可以在解决问题的时候&#xff0c;方便回顾和查找&#x…

使用webpack-bundle-analyzer分析uni-app 的微信小程序包大小(HbuilderX运行)

1、找到vue.config.js 文件。如果找不到&#xff0c;则在项目根目录下&#xff08;跟pages.json同一个目录下&#xff09;创建一个JS文件&#xff0c;命名为vue.config.js 2、安装webpack-bundle-analyzer&#xff0c;官方网站&#xff1a;https://github.com/webpack-contrib…

中国汽车工业协会软件分会中国汽车基础软件生态标委会第三届二次会议在天津顺利召开

2023年8月30日&#xff0c;中国汽车工业协会软件分会中国汽车基础软件生态标委会&#xff08;AUTOSEMO&#xff09;第三届二次会议在天津召开。出席本次会议的有AUTOSEMO专业指导主任许艳华、中国汽车工业协会软件分会理事长、AUTOSEMO轮值主席、中汽创智CEO李丰军、经纬恒润协…

ChatGPT AIGC总结Excel中Vlookup,lookup,xlookup的区别

在Excel的使用过程中,查找函数是非常重要的,如Vlookup,lookup,Xlookup,index+match等都是使用的最多的函数,我们让ChatGPT,AIGC用思维导图来总结一下,各查找函数的用法与区别。 AIGC ChatGPT ,BI商业智能, 可视化Tableau, PowerBI, FineReport, 数据库Mysql Oracle…

[网鼎杯 2020 朱雀组]phpweb call_user_func()

时间一跳一跳的 抓个包 很奇怪 结合上面的 date() 认为第一个是函数 我们随便输一个看看 发现过滤了 随便输一个 linux指令 发现报错了 call_user_func() 看看是啥 很容易理解 第一个参数是函数名 后面是 参数 那么这里就是 func 函数 p 数值 所以我们看看有什么办法可以…

毕业设计-基于微信小程序的智能垃圾分类回收系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 1 简介 视频演示地址&#xff1a; 基于微信小程序的智能垃圾分类回收系统&#xff0c;可作为毕业设计 小…

易优cms小程序插件升级到2.1版本

eyou小程序升级到2.1&#xff0c;主要新增一键获取手机号&#xff0c;统一返回格式等 升级内容&#xff1a; &#xff08;1&#xff09;对全部接口的返回格式做统一化设置 &#xff08;2&#xff09;新增一键获取手机号功能 &#xff08;3&#xff09;新增footprint足迹接口 &…

Win10 屏蔽键盘按键

有时候键盘进水一直触发或者经常误触某个按键&#xff0c;非常影响体验&#xff0c;Win10是支持直接屏蔽按键的 创建注册表 WinR 快捷键输入regedit 打开注册表进入到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout空白处右击新建一个二进制值名为Scan…