简体   繁体   English

在这种情况下,为什么闭包会导致 JavaScript 中的内存泄漏?

[英]Why does closure cause memory leak in JavaScript in this case?

I have this following code snippet:我有以下代码片段:


function outer() {
  let a

  return function inner() {
    
     a = new Uint8Array(100000)
     const b = new Uint16Array(100000)
  };
};
const fn = outer(); 
fn()

Took one heap snapshot, ran it in a about:blank page in chrome 103.0.5060.114, and took a second snapshot.拍摄了一个堆快照,在 chrome 103.0.5060.114 的 about:blank 页面中运行它,然后拍摄了第二张快照。 Comparing the two snapshots, I found that in the second snapshot, there is one more Uint8Array .比较两个快照,我发现在第二个快照中,多了一个Uint8Array That means a is retained in memory, so it leaked .这意味着a保留在内存中,所以它泄漏了 。 But I can't find any Uint16Array so b didn't leak.但我找不到任何Uint16Array所以b没有泄漏。 在此处输入图像描述 在此处输入图像描述

But I couldn't figure out why that Uint8Array is leaking because it doesn't seem like I can still reference it outside the outer function.但我无法弄清楚为什么Uint8Array会泄漏,因为我似乎仍然无法在outer函数之外引用它。 So According to the garbage collection algorithm, it should have been collected by now.所以按照垃圾回收算法,现在应该已经被回收了。

Haven't tested this in other browsers or in Node.尚未在其他浏览器或 Node.js 中对此进行测试。

@ITgoldman's explanation is right: a is retained because inner uses it, and fn === inner , and fn is still reachable. @ITgoldman 的解释是正确的: a被保留是因为inner使用它,并且fn === inner ,并且fn仍然可以访问。 This is not a leak.这不是泄漏。

a can be reached again after fn() finishes simply by calling fn() again. a可以在fn()完成后再次调用fn()再次到达。 b is just a local variable, so it goes out of scope when fn() returns. b只是一个局部变量,所以当fn()返回时它超出了范围。

An example how this is useful/required: consider the following slight modification of your snippet:这是如何有用/需要的示例:考虑对您的代码段进行以下轻微修改:

function outer() {
  let a = 0;

  return function inner() {
    a++;
    console.log(`counter is now ${a}`);
  };
};
const fn = outer(); 
fn()
fn()
fn()

Clearly, a should remain alive from one invocation of fn() to the next, so it can't be collected.显然,从fn()的一次调用到下一次调用, a应该保持活动状态,因此它不能被收集。

It doesn't matter whether inner / fn reads from a , writes to a , or does both. inner / fn是从a读取、写入a还是两者都做并不重要。 As long as it uses a in any way, a will remain alive as long as fn is alive.只要它以任何方式使用a ,只要fn存在, a就会保持存在。 This makes sense because there could be more than one function referring to it: you could have inner1 allocating new arrays and storing them in a , and inner2 doing stuff with these arrays.这是有道理的,因为可能有多个函数引用它:您可以让inner1分配新数组并将它们存储在a中,而inner2对这些数组进行处理。

(This is not a V8 question; all spec-compliant JavaScript engines are required to behave like this.) (这不是 V8 问题;所有符合规范的 JavaScript 引擎都必须这样做。)

Usually, a Lexical Environment is removed from memory with all the variables after the function call finishes.通常,在函数调用完成后,词法环境连同所有变量一起从内存中删除。 That's because there are no references to it.那是因为没有引用它。 As any JavaScript object, it's only kept in memory while it's reachable.与任何 JavaScript 对象一样,它仅在可访问时保存在内存中。

However, if there's a nested function that is still reachable after the end of a function, then it has [[Environment]] property that references the lexical environment.但是,如果有一个嵌套函数在函数结束后仍然可以访问,那么它具有引用词法环境的 [[Environment]] 属性。

You can more about how garbage collection works in closures here .您可以在此处详细了解垃圾收集在闭包中的工作原理。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM