一、浏览器内存泄漏
浏览器封装的V8引擎支持对JS进行解析,当程序运行(runtime)时,只要程序提出需要内存,例如声明赋值字符串、对象、数组等变量时;操作系统就必须给分配内存使用,对于持续运行的服务进程,变量不断增加,并且内存没有得到及时的释放,内存占有会越来越大,轻则影响系统性能,重则直接导致系统崩溃。
1、闭包加引用
let fn = () =>{
let a = 0
return () => {
return a++
}
}
let b = fn()
console.log(b()) // 0
console.log(b()) // 1
闭包在IE6下会造成内存泄漏,但是现在已经无须考虑了。虽然变量a无法被回收(相当于全局变量),但是这点内存无须特别考虑,值得注意的是闭包本身不会造成内存泄漏,但闭包过多很容易导致内存泄漏。
2、相互引用
let aa = {}
let bb = {}
aa.c = bb
bb.c = aa
解决办法:不要互相引用变量
4、全局未声明的变量
function fn1() {
aa = 1 // window.aa
console.log(aa)
}
fn2()
function fn2() {
this.bb = 1 // window.bb
console.log(bb)
}
fn2()
函数fn1和fn2均未声明,即将变量绑定到了全局对象window下,这样函数执行完毕均不能释放变量内存。
解决办法:使用严格模式,在js顶部加上‘use strict’
5、定时器和回调函数
let a = 2
setInterval(() =>{
console.log(a++)
}, 1000)
当不需要setInterval或者setTimeout时,定时器没有被clear,定时器的回调函数以及内部依赖的变量都不能被回收,造成内存泄漏。
6、DOM对象添加属性
document.getElementById('id')[property] = obj
DOM如果存在会一直绑定这个事件,解决办法如下:
window.onunload = function(){
document.getElementById('id')[property] = null
}
7、vue中的定时器,事件监听,$on等
解决办法:在beforeDestroy钩子中移除或关闭相关事件
借鉴链接内存泄漏
二、垃圾回收机制
变量的生命周期:当一个变量的生命周期结束之后它所指向的内存就应该被释放。JS有两种变量,全局变量和在函数中产生的局部变量(不包括被引用的变量)。局部变量的生命周期在函数执行过后就结束了,此时便可将它引用的内存释放(即垃圾回收),但全局变量生命周期会持续到浏览器关闭页面。垃圾收集器会检测不再继续使用的变量,然后释放其占用的内存,并按照固定的时间间隔周期性的执行。
现代浏览器垃圾回收机制主要有两种:标记清除和引用计数。
1、标记清除(mark-and-sweep)
目前主流IE,Firefox,Opera,Chrome和Safari等浏览器均使用标记清除的垃圾收集策略。
工作原理:是当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“离开环境”。标记“离开环境”的就回收内存。
工作流程:
1.垃圾回收器,在运行的时候会给存储在内存中的所有变量都加上标记。
2.去掉全局中的变量以及被环境中的变量引用的变量的标记。
3.被加上标记的会被视为准备删除的变量。
4.垃圾回收器完成内存清除工作,销毁那些带标记的值并回收他们所占用的内存空间。
2、引用计数(reference counting)
引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1,如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值的引用的变量又取得了另外一个值,则这个值的引用次数减1,当这个值的引用次数为0时,则说明没有办法再访问这个值了,因此就可以将其占用的内存空间回收回来。
let aa = [] // 数组[]引用1次
let bb = aa // 数组引用2次
bb = null // 释放内存,引用还剩下1次,即变量aa的引用还存在
这样简单的垃圾回收机制,非常容易出现循环引用问题导致内存不能被回收, 进行导致内存泄露等问题; 如:
let aa = {}
let bb = {}
aa.c = bb
bb.c = aa
var wraper = document.querySelector('#btn');
wraper.onclick = handle;
wraper = null;
该方法并不能解除dom点击事件,因为释放的是wraper引用的这个变量,实际对象DOM已经绑定了这个btn点击事件,举个例子
let a = {}
let b = a
b.btn = () =>{}
b = null
console.log(a)