分配内存----->使用内存----->释放内存
JS 有自动垃圾回收机制,那么这个自动垃圾回收机制的原理是什么呢? 其实很简单,就是找出那些不再继续使用的值,然后释放其占用的内存。
大多数内存管理的问题都在这个阶段。 在这里最艰难的任务是找到不再需要使用的变量。
因为自动垃圾回收机制的存在,开发人员可以不关心也不注意内存释放的有关问题,但对无用内存的释放这件事是客观存在的。 不幸的是,即使不考虑垃圾回收对性能的影响,目前最新的垃圾回收算法,也无法智能回收所有的极端情况。
官方解释:内存泄漏(memory leak)是指程序中己动态分配的内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
通俗点就是指由于疏忽或者错误造成程序未能释放已经不再使用的内存,不再用到的内存却没有及时释放,从而造成内存上的浪费。
例子:
使用闭包产生的内存浪费来举例,不懂闭包的可以先去看我的另一篇文章
JS---JS中的闭包详解_Cirrod的博客-CSDN博客
注意点:
new Array()用于创建数组,存放在堆中,变量arr是对这个数组的引用,指向数组,放在栈中
在浏览器查看程序占用堆的内存
ps:可以看到此时堆内存占用81mb,也就是存在两个Array(10000000)
修改代码进行内存释放:
在浏览器查看程序占用堆的内存
ps:可以看到此时堆内存占用1.4mb,也就是两个Array(10000000)已经被浏览器进行回收
当程序运行需要的内存超过了剩余的内存时, 就出抛出内存溢出的错误。
for (let index = 0; index < 10000000000; index++) {
let element = new Array(10000000)
}
在js中,一个未声明变量的使用,会在全局对象中创建一个新的变量;在浏览器环境下,全局对象就是window
function test() {
names = 'hsq'
}
test();
console.log(names);//hsq
相当于
function test() {
window.names = 'hsq'
}
test();
console.log(window.names);//hsq
解决办法:
①使用js严格模式
未声明但直接赋值的变量将失败
//js文件
"use strict";
function test() {
names = 'hsq'
}
test();
console.log(names);//报错
②变量使用完后将变量的内存释放
function test() {
names = 'hsq'
}
test();
console.log(names);//hsq
names = null
console.log(names);//null
定时器setinterval或者settimeout在不需要使用的时候,没有被clear,定时器无法被内存回收,导致定时器的回调函数及其内部依赖的变量都不能被回收,这就会造成内存泄漏。
案例:
图解:
如果后续 renderer 元素被移除,整个定时器实际上没有任何作用。 但如果你没有回收定时器,整个定时器依然有效, 不但定时器无法被内存回收, 定时器的回调函数和回调函数中的依赖也无法回收。在这个案例中定时器的回调函数和回调函数里面的的依赖变量(serverData )也无法被回收。
解决方案:当不需要interval定时器或者timeout定时器的时候,调用clearinterval或者cleartimeout清除定时器
虽然我们将dom元素已经移除,但是对dom元素的引用没有清除,也会造成内存泄露
案例:
let img=document.getElementById('img')
img.src='http://image.png'
document.body.removeChild(img)// 这时我们对于 #image 仍然有一个引用, Image 元素, 仍然无法被内存回收.
ps:
变量img存储的是dom元素(#img)的引用,我们将dom元素(#img)在文档树中清除掉后,dom元素(#img)已经不存在了,但对dom元素(#img)的引用还存在,也就是变量img还是存储着对dom元素(#img)的引用,也会造成内存泄露
解决方案:
利用null释放内存
let img=document.getElementById('img')
img.src='http://image.png'
document.body.removeChild(img)// 这时我们对于 #image 仍然有一个引用, Image 元素, 仍然无法被内存回收.
img=null;//清空dom元素的引用
注意点:
另外需要注意的一个点是,对于一个 Dom 树的叶子节点的引用。 举个例子: 如果我们引用了一个表格中的td元素,一旦在 Dom 中删除了整个表格,我们直观的觉得内存回收应该回收除了被引用的 td 外的其他元素。 但是事实上,这个 td 元素是整个表格的一个子元素,并保留对于其父元素的引用。 这就会导致对于整个表格的引用都无法进行内存回收。所以我们要小心处理对于 Dom 元素的引用。
闭包是函数运行时候所产生的机制,函数执行会形成一个全新的私有上下文,可以保护里面的私有变量和外界互不干扰(保护机制),当其他上下文占用了当前上下文中的变量,导致当前上下文的变量不被释放,即大家所认为的闭包,需要当前上下文的变量不能被释放,这样私有变量及它的值也不会被释放掉(保存机制),
下面是比较常见的闭包场景:
在 JS 开发中,我们会经常用到闭包,一个内部函数,有权访问包含其的外部函数中的变量。 下面这种情况下,闭包也会造成内存泄露:
ps:
关键在于,闭包之间是共享作用域的, //console.log(num)语句依赖fn函数的局部num变量,所以会产生闭包,导致num变量不能被内存回收。
解决方案:
参考文章:
JavaScript 闭包 内存泄漏与解决办法_yongzhi1u的博客-CSDN博客_js 闭包内存泄露
函数使用完后,手动释放内存,设置为null
一个原则:不用的东西,及时归还。(使用完后设置为null)
参考文章:
JS常见内存泄漏及解决方案解析
「前端进阶」JS中的内存管理 - 知乎