最近看了一下javascript垃圾回收机制的博客,现在对其进行简单的总结。垃圾回收:
是一个自动的垃圾回收机制垃圾回收的原理:
考虑某一些对象或者变量在未来运行中不被考虑,并且向这些对象要求归还内存。但是在垃圾回收中,最为困难的就是找到在未来不被使用的对象。
为了解决上述问题,我们可以采用以下两种方法、
一、引用计数法
如果对象a中存在对对象b的引用,则对象b上的引用数不为0,此时在垃圾回收时,不能进行销毁。
let objA = {name:"dmc"} //此时{name:"dmc"}的引用次数为1
let objB = objA //此时{name:"dmc"}的引用次数为2.
objA = 0 //此时{name:"dmc"}的应用此时为1
objB = 0 //此时{name:"dmc"}的引用次数为0
//此时当垃圾回收时,可以回收{name:"dmc"}的内存。
但是引用计数存在一个弊端,就是存在循环引用的问题。
function func1() {
let objA = {}
let objB = {}
objA.b = objB
objB.a = objA
}
正如上面的代码所示,此时存在一个函数,当函数执行完毕后,此时垃圾回收应该进行销毁操作,但是此时objA和objB两个都存在一个引用,此时这两个对象不能进行销毁。解决方法是:当对象使用完毕后,应该设置变量为null。
二、标记清除法
在javascript中最常用的垃圾回收方式是标记清除法,因为从2012年起,所有现代浏览器都使用标记清除的垃圾回收方法,除了低版本的IE采用的是引用计数法。那么什么是标记清除法,就是一个全局对象出发,在浏览器中是window,从中去查找全部可以访问到的对象并进行标记,这是标记的过程。然后是清除,就是对没有标记的对象进行清除,也就是清除那些不可达的对象。标记清除法存在一个弊端,就是我们清理垃圾后,得到的内存不是连续的空间,但是如果我们在某一时刻,需要大量连续的内存空间,该如何做?此时就涉及在标记清除法的基础上的另一种的垃圾回收方法——标记整理法,就是我们在进行垃圾处理的过程中,会对内存空间进行整理。但是这样的效率比标记清除法低,在计算机中很多做法都是相互妥协的结果。
三、内存泄漏内存泄漏
:内存泄漏是指计算机可用的内存越来越少,主要是因为程序不能释放那些不能使用的内存。下面我们总结一下内存泄漏的几点原因:1、循环引用
上面我们提到到循环引用就是两个对象之间相互引用对方,导致内存无法释放。再次强调,一旦数据不使用,我们应当将其赋值为null。2、无意的全局变量
function foo() {
const lov = "dmc"
}
上面foo函数我们在执行时会创建一个函数作用域,当函数执行完毕后,此时该函数作用域(其实是一个对象)会被销毁。这是正常的情况。下面我们看一下创建无意的全局变量。
function foo(){
lov = "dmc"
}
上面foo函数在执行时不仅会创建一个函数作用域,并且由于lov变量没有声明,此时会在window上进行定义,所以window.lov="dmc"
,当函数执行结束后,此时window全局对象上的lov
属性并不能清除,此时造成内存泄漏。
function foo(){
this.lov = "dmc"
}
如上面代码所示,此时当函数执行时,this
指向window
。3、被忽略的计时器和回调函数
let someResource = getData()
setInterval(() => {
const node = document.getElementById("node")
if(node) {
node.innerHTML = JSON.stringify(someResource)
}
}, 1000)
上面的例子中,我们使用定时器,此时并且在定时器回调函数中使用外部变量,此时我们在进行操作时,每一个回调函数都会产生对其的引用,并且在执行定时操作时,回调函数以及其内部的变量都不会被销毁。此时造成内存泄漏,当我们不使用定时操作时,可以使用clearInterval
来删除定时器。4、无用DOM 元素的引用
在IE8以下版本的浏览器,DOM对象通常回合javascript之间产生循环引用的关系。下面这个例子。
function handlerSet() {
let ele = document.getElementById("ele")
ele.onclick = function() {
console.log("在这里执行相关操作")
}
}
上面的ele
通过onclick
来引用函数,函数存在外部引用来引用这ele
对象,此时造成循环引用,造成内存泄漏。不过现在不需要担心这种情况。因为现在浏览器不使用引用计数法,采用的是标记整理法来进行垃圾回收。
下面我们在看一个例子:
const btn = document.querySelector("button")
document.body.removeChild(document.querySelector("button"))
此时我们以为已经删除该DOM元素,其实不然,因为我们在处理任然存在一个变量指向该DOM元素。此时我们应该设置btn = null
还存在一种情况:如果我们存在列表元素ul和li
,此时如果我们取出其中的一个标签li
,并且删除ul
,此时整个列表不会删除,因为li
和其父元素ul
标签至今存在引用关系。由此导致内存泄漏。5、不合理的闭包导致内存泄漏
四、良好的实践
优化内存最好的方式是保留内存中仍然使用的数据,将不需要的数据设置为null
。
减少内存的方法就是创建对象,例如const obj = {}
,const arr = []
都为创建对象,如果我们不需要使用某一个数组时,可以将数组设置为[].length = 0
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/weixin_47450807/article/details/122912297
内容来源于网络,如有侵权,请联系作者删除!