JS 垃圾回收机制
当前主流的垃圾回收机制是标记清除(mark-and-sweep)
标记清除基于可达性
如果一个值可以从根开始通过引用或引用链被访问到,则认为该值是可达的。
下面这些都可以被看做是根(roots):
- 当前执行的函数,它的局部变量和参数。
- 当前嵌套调用链上的其他函数、它们的局部变量和参数。
- 全局变量。
垃圾回收器定期执行以下“垃圾回收”步骤:
- 垃圾收集器找到所有的根,并“标记”(记住)它们。
- 然后它遍历并“标记”来自它们的所有引用。
- 然后它遍历标记的对象并标记 它们的 引用。所有被遍历到的对象都会被记住,以免将来再次遍历到同一个对象。
- ……如此操作,直到所有可达的(从根部)引用都被访问到。
- 没有被标记的对象都会被删除。
还有一种过时的垃圾回收机制是引用计数,如果引用数为 0 则被清除,但有缺陷,例如循环引用就不会被清除
function foo() {
var obj1 = {};
var obj2 = {};
obj1.x = obj2 ; // obj1引用obj2
obj2.x = obj1 ; // obj2引用obj1
return true ;
}
foo()
内存泄漏
常见的场景
全局变量
function foo(arg){
bar =“some text”; // bar将泄漏到全局.
}
被遗忘的定时器和回调函数
var someResource = getData();
setInterval(function() {
var node = document.getElementById('Node');
if(node) {
node.innerHTML = JSON.stringify(someResource));
// 定时器也没有清除
}
// node、someResource 存储了大量数据 无法回收
}, 1000);
DOM 引用 ```javascript var refA = document.getElementById(‘refA’); document.body.removeChild(refA); // dom删除了 console.log(refA, “refA”); // 但是还存在引用,能console出整个div 没有被回收
// 解决办法:refA = null;
4. 闭包错误使用
```javascript
var theThing = null;
var replaceThing = function () {
var originalThing = theThing;
var unused = function () {
if (originalThing)
console.log("hi");
};
theThing = {
longStr: new Array(1000000).join('*'),
someMethod: function () {
console.log(someMessage);
}
};
};
setInterval(replaceThing, 1000);
解决办法
组件销毁时及时清除不必要的全局引用和回调函数,销毁定时器,解绑自定义事件,解绑DOM事件监听等等
WeakMap 和 WeakSet
WeakMap 的键只能是对象,是弱引用,可以被垃圾回收
WeakSet 只能是对象的集合,也可以被垃圾回收