IFE 2019年04月23日 分类: 前端知识体系 标签: javascript 浏览器 浏览量: 3067
代码里不再需要的,即为垃圾。
所有全局变量,都不是垃圾,因为任何时候都可能用到它。
局部变量,在函数退出的时候就变成垃圾,因为再也用不到了。
代码举例:
var a = 1 // 任何时候都可能用到 function fn(){ var b = 2 // 函数fn执行完就没有用了 console.log(b) } fn() c = {name: 'foo'} // 变量c保存的是{name: 'foo'}对象的内存地址 c = null // 当c设为null时,c和{name: 'foo'}之间的联系被切断
当fn函数执行的时候,会临时创建一个作用域,当fn执行完时就消失,在这个临时作用域中会创建一个变量b,并把它赋值为2。
在fn执行期间,b不能被回收,因为fn使用了b。 但fn执行完了后,b就没有用了,应该被销毁。
当再次执行fn的时候,会重新生成一个新的变量b,跟之前创建的b是不同的变量。
当c设为null时,c和{name: 'foo'}之间的联系被切断,如果{name: 'foo'}没有被别的地方引用,可以理解为不再被需要,变成了垃圾
如果希望将环引用的内容变成垃圾,将所有指向它的引用删掉即可。它指向别人是无所谓的。
如果希望删除整个环引用,就要把环与外界的唯一联系给切断。那这个环就变成了垃圾。
再次强调一下:如果有可能用到的,就不是垃圾;如果没有可能再用到,就是垃圾。
js里面有一些永远不会变为垃圾的东西,比如:
1. widow对象,只要进入一个页面,在页面关闭之前会一直存在,没有办法消灭,它有一些属性如:Object、Array、Promise...随时可能会用到,只要页面不关,必须在内存中长驻,不能被垃圾回收。
2. 全局变量,它们是全局作用域的变量,如上面代码里的a,fn
总结:一般来说,没有被引用的对象就是垃圾,应该被回收清除吗。如果有几个对象相互循环引用,形成环,但没有和外界联系,也是垃圾。
标记清除算法
找垃圾就是找没有引用的地方,只要把有引用的地方找到了,其他地方删掉就行了。
浏览器从global开始,去找它每一个引用,并记录。没有记录的就删除。
缺点:每隔一段时间就要重新标记,如果要标记的对象很多,需要时间,js是单线程的语言,浏览器在标记这些对象的时候,js不能执行。
改进:
1,分代垃圾回收:对象分为新、旧两组,那些能够存活足够长的对象不经常检查,比如global。如果执行一个函数里面有几个变量,执行完了就可以回收,不需要再从头遍历,这种临时调用的函数就是新生代的对象,马上标记马上删除。
2,增量搜集:引擎试图将垃圾收集分成几部分。然后分别逐个执行这些部分。这需要一些额外的标记记来跟踪变化,但我们有很多微小的延迟,而不是一个大的延迟。
3,空闲时间收集:垃圾收集器尝试仅在CPU空闲时运行,以减少对执行的可能影响。
优化内存:
页面占有最少的内存可以获得更好的性能,为了优化内存占用,最好给执行中的代码只保存必要的数据,一旦数据不再有用,最好通过将值设置为null来释放其引用,作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。这种做法叫解除引用。适用于大多数全局变量和全局对象的属性,局部变量会在它们离开执行环境时自动被解除引用。
现代浏览器使用的都是标记清除式的垃圾回收策略,只不过垃圾收集的时间间隔不同。
引用计数
记录每个值被引用的次数,回收引用次数引用次数为零的值所占内存。但如果存在循环引用,则引用次数永远不会是0,可能会导致大量内存得不到回收。
IE9之前,BOM和DOM是使用C++以COM(Component Object Model,组件对象模型)对象的形式实现的,不是原生的JavaScript对象,COM对象的垃圾收集机制采用的就是引用计数策略。只要涉及COM对象,就会存在循环引用的问题,可能导致内存泄漏。不过IE9开始把BOM和DOM对象都转换成了真正的JavaScript对象。
let div1 = document.getElementById('div1') div1.onclick = function(){ // ... } setTimeout(function(){ // div1.remove() // console.log(div1.onclick) // div1 = null })
div1.remove()
后感觉上,它绑定的函数应该被回收。但把div1从页面里删掉,并不是从内存里删掉,因为某个时刻还可能用到div1,所以它绑定的onclick方法不会被删除。
div1 = null
后,感觉上onlcick方法的函数也该被清除回收,但页面上那个div元素还在,这个设为null的div1只是一个js变量,而不是页面的DOM,虽然设为null后不引用那个DOM,但DOM还真实存在。
只有同时使用div1.remove()
和div1 = null
,代表从内存和页面中都删掉,那onclick方法的函数就会被删除。(IE有bug,就算DOM不在页面中,但DOM引用了onclick,会认为DOM还在被使用,就不能消失。导致onclick也不能消失,只能手动删除div1.onclick = null
)
在浏览器里,即使所有的全局变量都没引用,也不代表元素能被删除,因为有可能DOM元素引用了这个函数。