title | date | tags |
---|---|---|
JavaScript篇-垃圾回收机制 |
2018-10-08 08:27:10 -0700 |
为什么要有垃圾回收这鬼玩意 ?由于字符、数组没有固定的大小所以当他们的大小已知时,才能对他们进行动态的存储分配。js在每一次创建字符串、数组、对象等时,解释器必须分配内存来存储那个实体。只要分配了内容,最终都要释放,不然解释器将会消耗完系统中所有可用的内存。导致系统崩溃(好像很牛逼的样子)
各大浏览器通常用采用的垃圾回收有两种方法:标记清除、引用计数
-
这是js最常用的垃圾回收方法,当变量进入执行环境时,就标记着这个变量为 “ 进入环境 ” (come on ~),从逻辑上来讲不能释放 “进入环境” 的变所占用的内存,因为只要变量进入环境,意味着可能会用到该变量。当变量离开环境则标记为 “ 离开环境 ”
-
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的标记。而在此之后在被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后。垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。
- 引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。 相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。
博主之前面试,有个题目就是问如果两个对象通过各自的属性相互引用,会出什么情况呢 ? 如何解决 ?
function problem() {
var objA = new Object();
var objB = new Object();
objA.someOtherObject = objB;
objB.anotherObject = objA;
}
在这个例子中,objA和objB通过自身属相互引用,也就是说这两个对象的引用次数都是2。在采用引用计数的策略中,由于函数执行之后,这两个对象都离开了作用域,函数执行完成之后,objA和objB还将会继续存在,因为他们的引用次数永远不会是0。这样的相互引用如果说很大量的存在就会导致大量的内存泄露。
如何解决呢 ? 手切断他们的循环引用 !
objA.someOtherObject = null
objB.anotherObject = null
在这个例子中,objA和objB通过自身属相互引用,也就是说这两个对象的引用次数都是2。在采用引用计数的策略中,由于函数执行之后,这两个对象都离开了作用域,函数执行完成之后,objA和objB还将会继续存在,因为他们的引用次数永远不会是0。这样的相互引用如果说很大量的存在就会导致大量的内存泄露。
垃圾收集器是周期性运行的,而且如果为变量分配的内存数量很可观,那么回收工作量也是非常大的。像IE的垃圾收集器是根据内存分配量运行的,具体一点说就是256个变量,4096个对象(或数组)字面量和数组元素(slot)或者64KB的字符串,上述任何一个临界值,垃圾收集器就会运行。在IE7发布之后,JavaScript引擎的垃圾收集改变了工作方式: 触发垃圾收集的变量分配、字面量和数组元素的临界值被调整为动态修改,如果垃圾收集例程回收的内存分配量低于15%,则变量、字面量和数组元素的临界值就会加倍。也就是说,垃圾收集例程回收了85%的内存分配量,则将各种临界值重置回默认值
分配给Web浏览器的可用内存数量通常要比分配给桌面应用程序的少,目的是: 出于安全方面的考虑,防止运行JavaScript的网页耗尽全部系统内存而导致系统奔溃。因此,确保占用最少的内存可以让页面获得更好的性能,而优化内存占用的最佳方式,就是为执行中的代码只保存必要的数据, 一旦数据不再有用,则将值设置为 null
来释放这个引用,这种方式就是 接触引用
局部变量会在他们离开执行环境时自动被解除引用
function createPerson(name) {
var localPerson = new Object()
localPerson.name = name
return localPerson
}
var globalPerson = createPerson('彭道宽')
// 手动解除 globalPerson 的引用
globalPerson = null
上诉例子中,变量 globalPerson 取得了 createPerson() 函数返回的值。在createPerson() 内部,我们创建了一个对象并将其赋给局部变量localPerson, 然后又为该对象添加了一个名为 name 的属性。
最后调用这个函数时,localPerson 以函数值的形式返回并赋给全局变量 globalPerson。由于 localPerson 在 createPerson() 函数执行完毕之后,就离开了其执行环境,因此,我们不需要去为它解除引用,但是对于全局变量 globalPerson 来说,则需要我们在不使用它的时候,手动解除它的引用。
解除一个值的引用并不意味着自动回收该值所占用的内存。解除引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。