javascript - Javascript 闭包是保留整个父词法环境还是仅保留闭包引用的值的子集?

标签 javascript node.js v8

这个问题在这里已经有了答案:





About closure, LexicalEnvironment and GC

(3 个回答)


2年前关闭。




考虑以下示例:

function makeFunction() {
  let x = 3;
  let s = "giant string, 100 MB in size";

  return () => { console.log(x); };
}

// Are both x and s held in memory here
// or only x, because only x was referred to by the closure returned
// from makeFunction?
let made = makeFunction();

// Suppose there are no further usages of makeFunction after this point

// Let's assume there's a thorough GC run here

// Is s from makeFunction still around here, even though made doesn't use it?
made();

因此,如果我只关闭父词法环境中的一个变量,该变量是否保留,或者其词法环境中的每个兄弟变量也保留?

另外,如果 makeFunction 本身嵌套在另一个外部函数中,即使 makeFunction 和 makeFunction 的返回值都没有引用该外部词法环境中的任何内容,该外部词法环境会被保留吗?

我问的是性能原因 - 闭包是保留一堆东西还是只保留它们直接引用的东西?这会影响内存使用和资源使用(例如打开的连接、句柄等)。

这主要是在 NodeJS 上下文中,但也可以在浏览器中应用。

最佳答案

V8 开发人员在这里。这有点复杂;-)

简短的回答是:闭包只保留他们需要的东西。

所以在你的例子中,在 makeFunction 之后已运行,s 引用的字符串将有资格进行垃圾收集。由于垃圾收集的工作原理,无法预测它何时会被释放。 “在下一个垃圾收集周期”。是否makeFunction再次运行没关系;如果它再次运行,将分配一个新字符串(假设它是动态计算的;如果它是源中的文字,那么它会被缓存)。是否made已经运行或将再次运行也没关系;重要的是你有一个引用它的变量,所以你可以(再次)运行它。引擎通常无法预测 future 哪些功能会或不会被执行。

更长的答案是有一些脚注。一方面,正如评论已经指出的那样,如果您的闭包使用 eval , 然后一切都必须保留,因为无论源代码片段是 eval 'ed 可以引用任何变量。 (关于可能引用 eval 的全局变量的评论中提到的内容并不正确;“全局评估”(也称为“间接评估”)存在语义差异:它看不到局部变量。这通常被认为是一个优势为了性能和可调试性——但更好的是根本不使用 eval。)

另一个脚注有点不幸的是,跟踪并没有尽可能细粒度:每个闭包都将保持任何闭包所需的内容。我们已经尝试解决这个问题,但事实证明,更细粒度的跟踪会导致更多的内存消耗(用于元数据)和 CPU 消耗(用于执行工作),因此对于实际代码通常不值得(尽管它可能会对人工测试正是强调这种情况)。举个例子:

function makeFunction() {
  let x = 3;
  let s = "giant string, 100 MB in size";
  let short_lived = function() { console.log(s.length); }
  // short_lived();  // Call this or don't, doesn't matter.
  return function long_lived() { console.log(x); };
}

let long_lived = makeFunction();

在这个修改后的例子中,即使 long_lived仅使用 x , short_lived确实使用 s (即使它从未被调用过!),并且只有一个桶用于“来自 makeFunction 的局部变量,这些变量是某些闭包所需的”,因此该桶同时保留了 xs活。但正如我之前所说:真正的代码很少会遇到这个问题,所以这通常是你不必担心的。

边注:

and also resource usage (e.g. open connections, handles, etc.)



作为一个非常笼统的陈述(即,在任何语言或运行时环境中,无论闭包或其他什么),通常建议不要依赖垃圾收集来进行资源管理。我建议在适合释放资源时立即手动明确地释放资源。

关于javascript - Javascript 闭包是保留整个父词法环境还是仅保留闭包引用的值的子集?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59277687/

相关文章:

javascript - Express 中不清楚的错误处理

node.js - 控制台日志会增加nodejs服务器上的内存吗?

c++ - 如何在线程中使用 v8?

javascript - 带有 AJAX 和 DOM 处理 API 的下拉菜单

javascript - 如何重命名Webpack中每个入口点的output.library选项?

javascript - HTML 列表 : How can I count the number elemnts retrieved in the list

node.js - 更新记录在数组 Node.js + MongoDB 中推送唯一值

node.js - 是否有可能破坏上下文?

javascript - NodeJs、AngularJs、Mongoose 在 Function.keys( native )的非对象上调用错误 : TypeError: Object. 键

javascript - 删除动态创建的行 : removechild