JS 垃圾回收机制
当前主流的垃圾回收机制是标记清除(mark-and-sweep) 
标记清除基于可达性
如果一个值可以从根开始通过引用或引用链被访问到,则认为该值是可达的。
下面这些都可以被看做是根(roots):
- 当前执行的函数,它的局部变量和参数。
 - 当前嵌套调用链上的其他函数、它们的局部变量和参数。
 - 全局变量。
 
垃圾回收器定期执行以下“垃圾回收”步骤:
- 垃圾收集器找到所有的根,并“标记”(记住)它们。
 - 然后它遍历并“标记”来自它们的所有引用。
 - 然后它遍历标记的对象并标记 它们的 引用。所有被遍历到的对象都会被记住,以免将来再次遍历到同一个对象。
 - ……如此操作,直到所有可达的(从根部)引用都被访问到。
 - 没有被标记的对象都会被删除。
 
还有一种过时的垃圾回收机制是引用计数,如果引用数为 0 则被清除,但有缺陷,例如循环引用就不会被清除
function foo() {var obj1 = {};var obj2 = {};obj1.x = obj2 ; // obj1引用obj2obj2.x = obj1 ; // obj2引用obj1return 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. 闭包错误使用```javascriptvar 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 只能是对象的集合,也可以被垃圾回收
