我想了解在哪种情况下,没有进一步使用的变量存储在闭包中并导致内存泄漏。我最喜欢的结果是"没有",但似乎并非如此。
据我所知,一旦函数在另一个函数内声明,其内部[[scope]]被赋予其封装函数的LexicalEnvironment。此LexicalEnvironment在此时引用局部变量和整个范围链。这基本上包括函数可以访问的所有自由变量(根据我对lostechies, javascript closures explained的理解)。
这里出现了第一个问题:这应该意味着所有只要函数存在就可以达到这些变量。例如。以下应该已经泄漏:
function a() {
let big = new Array(1000000).join('*'); //never accessed
//function unused() { big; }
return () => void 0;
}
let fstore = [];
function doesThisLeak() {
for(let i = 0; i < 100; i++) fstore.push(a());
}
doesThisLeak();
&#13;
幸运的是,我的firefox似乎并非如此。我已经收到了几个解释为什么它不会泄漏,从#34;抖动是聪明的&#34; to&#34; LexicalEnvironment是一种记录类型,这意味着GC可以收集未使用的变量&#34;。我仍然不知道其中一个是否正确,这是否在所有现代运行时间都没有泄漏以及原因。
经过进一步的研究,我发现auth0, four types of leaks in javascript(遗憾的是,似乎没有跳转到的html id,相关的部分是&#34; 4:Closures&#34;)这显示了一种欺骗任何东西的方法聪明的事情是收集未使用的变量。在上面的片段中,当只是取消注释&#34;未使用的&#34;功能,我没有看到RAM使用率再次下降(已经注意到它可能是GC因为其他原因而没有运行。但是,到目前为止,我假设它泄漏。我也被告知这仅限于firefox ,但它似乎在chrome中产生类似的行为
这个例子(如果它确实做了我认为它做的事情),表明由于同一范围内的函数声明,完全未使用的变量可能会泄漏。
结束我的问题:
答案 0 :(得分:7)
编译器可以检查返回函数的代码以查看它引用的自由变量,并且只需要将这些变量保存在闭包中,而不是整个LexicalEnvironment。您可以通过检查Javascript调试器中的闭包来看到这一点。
function a() {
let big = new Array(1000000).join('*');
let small = "abc"; // is accessed
return (x) => small + x;
}
fun = a();
console.dir(fun);
function b() {
let big = "pretend this is a really long string";
function unused() { big; }
return () => void 0;
}
fun = b();
console.dir(fun);
在调试器中展开第一个函数时,您会在small
属性中看到Closure
,但不会看到big
。b()
。不幸的是,Chrome编译器似乎不够聪明,无法检测何时在未返回的未使用函数中引用变量,因此不需要保存它,因此我们在{{1}中出现泄漏}。
任何未保存在闭包中的数据都会变成垃圾并且可以被收集,因此它不会泄漏。
答案 1 :(得分:3)
致电a()
后,您的fstore
仅参考了已创建的功能() => void
。 a()
返回后,其范围变量将被删除。这意味着没有vars引用您的new Array(1000000).join('*')
并且它将被垃圾收集。如果取消注释unused
函数,它将以相同的方式收集,因为它也将被删除。您的代码中没有泄漏。
答案 2 :(得分:0)