什么是内存泄漏
指程序中己动态分配的堆内存由于某种原因未释放或无法被浏览器所回收,造成系统内存占用越来越大,最终导致程序运行缓慢甚至系统崩溃等严重后果。这种现象就叫做内存泄漏。
造成内存泄露的原因
总结如下
- 全局变量:使用未声明的变量,而意外的创建了一个全局
变量,而使这个变量一直留在内存中无法被回收- 闭包:不合理的使用闭包,从而导致某些变量一直被留在内存当中。
- 获取一个 DOM 元素的引用,而后面这个元素被删除,由于一直保留了对这个元素的引用,所以它也无法被回收
- 定时器没有清除
- 遍历循环时反复定义变量
1、尽量少定义全局变量
(全局变量)
因为全局变量在内存中会一直存在,除非页面关闭,因此大量的全局变量会导致内存的增加 ,同时不要注意意外定义的全局变量,比如说—未声明而定义的变量
function leak(){
leak="xxx"; // 等同于window.leak = "xxx",leak成为一个全局变量,不会被回收
}
解决:
-
方法一,使用严格模式,即在JavaScript 文件开头添加 “use strict”
<script> "use strict"; x = 3.14; // 报错 (x 未定义) </script>
-
方法二,手动释放内存,在使用完之后,对其赋值为 null
2、闭包引用的变量没有及时被清除
(闭包)
function bindEvent(){
var obj=document.createElement("XXX");
obj.οnclick=function(){
//Even if it's a empty function
}
}
闭包可以维持函数内局部变量,使其得不到释放。上例定义事件回调时,由于是函数内定义函数,并且内部函数–事件回调的引用外暴了,形成了闭包。
解决
-
方法一,不要使用闭包
-
方法二,如果闭包是由于在函数内部定义了一个dom绑定的click事件,则绑定完成后,把对dom的引用定义为null,释放dom引用即可。
//在定义事件处理函数的外部函数中,删除对dom的引用
function bindEvent(){
var obj=document.createElement("XXX");
obj.οnclick=function(){
//Even if it's a empty function
}
obj=null;
}
3、对于那些已经被移除的dom, 仍然保持着对 dom 元素的引用,未被及时清除
(被移除的dom)
dom元素已经被移除,但 对 dom元素的引用没有解除,会导致内存泄漏。
解决办法:手工移除,将引用指向null
function removeButton() {
var btn = document.getElementById('button'); //引用dom元素
document.body.removeChild(btn); // 虽然我们用removeChild移除了button, 但是在btn对象里保存着#button的引用,换言之, DOM元素还在内存里面.
btn = null; //清空引用
}
4、被遗忘的定时器和回调函数
(定时器)
如果你没有清空定时器,整个定时器依然有效, 不但定时器无法被内存回收,
定时器函数中的依赖也无法回收
var someResouce=getData();
setInterval(function(){
var node=document.getElementById('Node');
if(node){
node.innerHTML=JSON.stringify(someResouce)
}
},1000)
5、遍历循环时反复定义变量
浏览器的垃圾回收机制
为了解决内存泄漏问题,js 引擎有了垃圾回收机制,能够让 js 自动的管理内存,将内存中不在使用的变量回收掉,然后释放出内存空间。
两种回收机制,
一个是标记清除法,
另一种是引用计数法,
其实他两的实现原理我们只明白一点就是都是通过判断当前的变量是否被引用,如果没有被引用,就说明该变量应该被回收。
什么是引用?
所谓的引用就是存储在堆内存中的对象你是直接不能访问的,而是通过栈内存中存储该对象的地址进行访问的,该地址就保持着对该对象的引用。
就好比一个盒子,盒子里有一块糖,糖和盒子外部有一根绳子连接着,如果你想直接打开盒子取出糖,不好意思,盒子被上锁了,如果你通过绳子将糖从盒子的小孔中取出来是可以的,就相当于我们所说的引用,糖就是所谓的对象,盒子相当于堆内存。
闭包真的会产生内存泄漏?
闭包就是一个函数,这个函数能够访问其他函数的作用域中的变量
function outer() {
var a = '变量1'
var inner = function () {
console.info(a)
}
return inner // inner 就是一个闭包函数,因为他能够访问到outer函数的作用域
}
闭包产生了内存泄漏,对吗??
其实,闭包不是真正产生内存泄漏的原因!
实际上我们大多遇到的闭包产生内存泄漏问题的根本原因是
没有及时的断开对变量的引用、或者是没有及时清空变量
参考链接 https://blog.csdn.net/michael8512/article/details/77888000