javascript垃圾回收机制

什么需要被释放?

  1. 字符串
  2. 数组
  3. 对象

为什么它们需要被释放?

因为它们没有固定大小,只有当它们大小已知的时候,才能对它们进行动态的存储方案。
因此JS每次创建它们时,都必须分配内存,这就是动态分配内存。
只要是动态分配内存的,都需要被释放。否则,JS的解释器将会消耗完系统中所有可用的内存,造成系统崩溃

例子:

1
2
3
var a = 'before'
var b = 'overwrite a'
var a = b

以上这个例子中,before这个字段失去了引用,系统检测到这个事实之后,就会释放该字符串的存储空间以便这些空间可以被再利用。

怎么垃圾回收?

1、 标记清除(todo 这个解释不是很清晰)
当变量进入执行环境时,就标记这个变量为“进入环境”。当变量离开环境时,则标记“离开环境”。理论上永远不会释放“进入环境”的变量,因为很可能会被用到。
垃圾收集器在运行的时候,会给存储在内存中的所有变量都加上标记。然后,它会去掉环境中的变量以及被环境中的变量引用的标记。在此之后,如果变量再被加上标记,则该变量将被视为准备删除的变量,因为环境中的变量已经无法访问到这些变量了。最后。垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收它们所占用的内存空间。

2、 引用计数(不太常见)
引用计数的含义是,跟踪记录每个值被引用的次数。当声明了一个变量的并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量被赋值成了另一个值,则这个值的引用次数就减1。当引用次数变为0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。

引用计数存在一个问题,如果两个对象相互引用了,则引用次数永远是1,不会被垃圾回收机制所回收。

1
2
3
4
var a = {};
var b = {};
a.someProperty = b;
b.someProperty = a;

大量这样的相互引用就会导致大量的内存泄漏。

这个问题不太好避免。例如,DOM和BOM是用C++的COM(Component Object Modal,组件对象)对象实现的,而COM对象的垃圾回收器就是采用的引用计数的策略。因此,即使Javascript的垃圾回收器是使用标记清除的策略来实现的,只要访问了COM对象,依然会存在循环引用的问题。看下这个例子。

1
2
3
4
var elem = document.getElementById('element');
var obj = {};
obj.element = elem;
elem.obj = obj;

在这个例子中,一个DOM元素与一个原生JS对象之间建立了相互引用。由于相互引用,即使将elem这个DOM元素从页面中删除,内存也永远不会被回收。

我们可以手动切断循环引用来解决这个问题

1
2
obj.element = null;
elem.obj = null;

怎么减少垃圾内存

1、 对象object优化
避免使用someObj = {}来清空对象,因为这将会导致过多的内存创建。可以通过清空一个对象,再在此对象上添加属性的方式来达到复用对象的目的,从而节省内存。

1
2
3
4
5
6
7
function clearObj(obj){
for(let key in obj){
if(obj.hasOwnProperty(key)){
delete obj[key];
}
}
}

2、 数组array优化
同理,避免使用someArr = []来清空数组,可以使用someArr.length = 0来清空一个数组并复用它,来达到节省内存的目的。

3、 function优化
如果一个function总是被重复创建和使用,可以用一个变量先保存它,从而达到节省内存的目的。

1
2
3
4
this.func = function(){
console.log(Date.now());
}
setTimeout(this.func, 1000);

坚持原创技术分享,您的支持将鼓励我继续创作!