万恶的前端内存泄漏及万善的解决方案「详解」

最近收到测试人员的反馈说我们开发的页面偶现卡死,点击无反应的情况,特别是打开页面较久的时候发生概率较高。打开任务管理器,看到内存占有率已经很高了,初步判断可能存在内存泄漏的情况。下面排查内存泄漏的原因。
系统进程不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)。当内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。Chrome 限制了浏览器所能使用的内存极限(64 位为 1.4GB,32 位为 1.0GB)
引起内存泄漏的原因
1、意外的全局变量
由于 js 对未声明变量的处理方式是在全局对象上创建该变量的引用。如果在浏览器中,全局对象就是 window 对象。变量在窗口关闭或重新刷新页面之前都不会被释放,如果未声明的变量缓存大量的数据,就会导致内存泄露。
未声明变量
function fn() {
a = ‘global variable’
}fn()复制代码
使用 this 创建的变量(this 的指向是 window)。
function fn() {
this.a = ‘global variable’
}fn()复制代码
解决方法:
避免创建全局变量
使用严格模式,在 JavaScript 文件头部或者函数的顶部加上 use strict。
2、闭包引起的内存泄漏
原因:闭包可以读取函数内部的变量,然后让这些变量始终保存在内存中。如果在使用结束后没有将局部变量清除,就可能导致内存泄露。
function fn () {
var a = “I’m a”;
return function () {
console.log(a);
};
}复制代码
解决:将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中。
比如:在循环中的函数表达式,能复用最好放到循环外面。
// badfor (var k = 0; k < 10; k++) {
var t = function (a) {
// 创建了10次 函数对象。
console.log(a)
}
t(k)
}
// goodfunction t(a) {
console.log(a)
}for (var k = 0; k < 10; k++) {
t(k)
}
t = null复制代码
3、没有清理的 DOM 元素引用
原因:虽然别的地方删除了,但是对象中还存在对 dom 的引用。
// 在对象中引用DOMvar elements = {
btn: document.getElementById(‘btn’),
}function doSomeThing() {
elements.btn.click()
}
function removeBtn() {
// 将body中的btn移除, 也就是移除 DOM树中的btn
document.body.removeChild(document.getElementById(‘button’))
// 但是此时全局变量elements还是保留了对btn的引用, btn还是存在于内存中,不能被GC回收
}复制代码
解决方法:手动删除,elements.btn = null。
4、被遗忘的定时器或者回调
定时器中有 dom 的引用,即使 dom 删除了,但是定时器还在,所以内存中还是有这个 dom。
// 定时器var serverData = loadData()setInterval(function () {
var renderer = document.getElementById(‘renderer’)
if (renderer) {
renderer.innerHTML = JSON.stringify(serverData)
}
}, 5000)
// 观察者模式var btn = document.getElementById(‘btn’)function onClick(element) {
element.innerHTMl = “I’m innerHTML”
}
btn.addEventListener(‘click’, onClick)复制代码
解决方法:
手动删除定时器和 dom。
removeEventListener 移除事件监听
vue 中容易出现内存泄露的几种情况
在 Vue SPA 开发应用,那么就更要当心内存泄漏的问题。因为在 SPA 的设计中,用户使用它时是不需要刷新浏览器的,所以 JavaScript 应用需要自行清理组件来确保垃圾回收以预期的方式生效。因此开发过程中,你需要时刻警惕内存泄漏的问题。
1、全局变量造成的内存泄露
声明的全局变量在切换页面的时候没有清空

这里是首页
复制代码 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 来实现。 // 代码1 ele.addEventListener('click', handler, false) // 代码2const listener = new WeakMap() listener.set(ele, handler) ele.addEventListener('click', listener.get(ele), false)复制代码 代码 2 比起代码 1 的好处是:由于监听函数是放在 WeakMap 里面,一旦 dom 对象 ele 消失,与它绑定的监听函数 handler 也会自动消失。 作者:lzg9527
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值