JS常见内存泄漏及解决方案解析
浏览器的垃圾回收机制
在 JS 中创建一个变量的时候,系统会根据,变量的类型,自动为其分配对应的内存(基础类型 -> 栈内存,固定大小;对象类型 -> 堆内存,根据需要分配大小)。
正常情况下,当这些变量不再被使用的时候,就会被回收,内存被释放。
浏览器的垃圾回收机制及原理
内存泄漏?
官方解释:内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
通俗点就是指由于疏忽或者错误造成程序未能释放已经不再使用的内存,不再用到的内存却没有及时释放,从而造成内存上的浪费。
造成内存泄漏的几种情况:
1. 定义全局变量
在局部作用域中,等函数执行完毕,变量就没有存在的必要了,垃圾回收机制很亏地做出判断并且回收,但是对于全局变量,很难判断什么时候不用这些变量,无法正常回收;所以,尽量少使用全局变量。
意外的全局变量:
function foo() {
a = 'test'
}
// 上面的写法等价于
function foo() {
window.a = 'test'
}
function foo() {
this.a = 'test'
// 函数自身发生调用,this指向全局对象window
}
foo();
上面的a变量应该是foo()内部作用域变量的引用,由于没有使用var来声明这个变量,这时变量a就被创建成了全局变量,这个就是错误的,会导致内存泄漏。
解决方式:使用 var let const 来定义变量。或者在js文件开头添加 ‘use strict',开启严格模式。
- 闭包(函数套函数,子函数引用了父函数的参数或变,并且被外部引用,形成不被释放的作用域,称之闭包)
在使用闭包的时候,就会造成严重的内存泄漏,因为闭包中的局部变量,会一直保存在内存中。
// 闭包维持了 onclick 方法的内部变量,并且这个绑定在了 DOM 上。
function bindEvent() {
var obj = document.querySelector("#xxx");
obj.onclick = function () { };
};
// 解决方案1
function bindEvent() {
var obj = document.querySelector("#xxx");
obj.onclick = onClickHandler;
};
function onClickHandler () {};
// 解决方案2
function bindEvent() {
var obj = document.querySelector("#xxx");
obj.onclick = function () { };
obj.onclick = null;
}
-
定时器
定时器setInterval或者setTimeout在不需要使用的时候,没有被clear,导致定时器的回调函数及其内部依赖的变量都不能被回收,这就会造成内存泄漏。
解决方式:当不需要interval或者timeout的时候,调用clearInterval或者clearTimeout -
事件监听
DOM.addEventListener("click", callback)
垃圾回收机制不好判断该事件是否需要被解除,导致 callback 不能被释放,此时需要手动解除绑定:DOM.removeEventListener(callback)
-
元素引用没有清理
var a = document.getElementById('id');
document.body.removeChild(a);
// 不能回收,因为存在变量a对它的引用。虽然我们用removeChild移除了,但是还在对象里保存着#的引用,即DOM元素还在内存里面。
解决方法: a = null;
- console
控制台日志记录对总体内存内置文件的影响,也是个重大的问题,同时也是容易被忽略的。记录错误的对象,可以将大量的数据保留在内存中。
传递给console.log的对象是不能被垃圾回收,所以没有去掉console.log可能会存在内存泄漏