V8 的垃圾回收主要基于可达性分析:从根对象(全局对象、当前执行栈中的局部变量)出发遍历引用链,不可达的对象即被回收。早期采用引用计数,但循环引用会导致对象永远无法回收;现代浏览器基本都改用标记清除,新生代用 Scavenge 算法复制存活对象,老生代用标记整理。
面试答这道题重点有两条线:一是算法演进,从引用计数到标记清除解决了循环引用;二是分代回收思想,短生命周期对象放新生代频繁清理,长生命周期对象晋升老生代。再结合闭包场景说明:只要闭包被持有,它引用的变量就不会被回收。
// 引用计数的缺陷:循环引用导致永远无法回收 function mkLeak() { const x = {}, y = {}; x.ref = y; y.ref = x; // 互相引用,两边计数都不为 0 return '引用计数法无法回收这两个对象'; } // 现代引擎改用可达性分析:从根出发不可达即回收 const gc = { alive: '从根节点可抵达的对象会被保留', dead: mkLeak() }; console.log(gc.alive, '||', gc.dead);