【问题标题】:examples of closures in node.js that would cause memory leaksnode.js 中会导致内存泄漏的闭包示例
【发布时间】:2012-02-07 14:35:24
【问题描述】:

在尝试调试 NodeJS 中的内存泄漏时,我发现它非常困难(鉴于缺乏我所知道的分析工具)。

我想我会回到基础,并确保我了解如何在 NodeJS 中专门创建内存泄漏。我对可能导致内存泄漏的闭包类型感到困惑,并且不确定垃圾收集器需要什么来释放该内存。

您能否举一些会导致 Node.js 内存泄漏的基本模式示例?

【问题讨论】:

    标签: javascript node.js


    【解决方案1】:

    确切地说不是“泄漏”,但这可能是一个常见的陷阱。

    var fn = (function() {
      var a = "super long string ...";
      var b = "useless value";
      var c = "Hello, World!";
    
      return function() {
        return c;
      };
    })();
    

    这将返回一个引用范围的函数,并且该范围内的每个本地变量都将被保留,即使只需要其中一个值。这会导致内存使用量超出您的需要,特别是如果您的函数使用一个小变量,但该范围内有您不需要继续引用的大值。


    如何解决?

    简单的选择是在函数结束时将你不关心的变量清空。变量仍在范围内,但它们的数据将被释放。

    var fn = (function() {
      var a = "super long string ...";
      var b = "useless value";
      var c = "Hello, World!";
    
      // do stuff with a and b
    
      a = b = null;
    
      return function() {
        return c;
      };
    })();
    

    或者你可以将任何使用临时变量的东西分解成它自己的函数,这样它们的作用域就可以被释放。对于大型项目,这是一个更好的解决方案。

    var doSetup = function() {
      var a = "super long string ...";
      var b = "useless value";
      // do stuff with a and b
    };
    
    var fn = (function() {
      doSetup();
    
      var c = "Hello, World!";
    
      return function() {
        return c;
      };
    })();
    

    【讨论】:

    • 如果它不返回 c 但像这样引用它会怎样:` var fn = (function() { var a = "super long string ..."; var b = "useless value"; var c = "Hello, World!"; function() { c = "something else"; }; })(); `
    • 重点是如果函数仍然被引用,创建它的闭包作用域会保存在内存中。该函数的实际作用对这一事实没有影响。
    • 那么,您将如何修复您发布的原始代码以确保它不再泄漏?
    • 我添加了一些示例,说明 2 种降低内存占用并完成相同任务的方法。
    • 这是一个非常理论上的“泄漏”。在实践中,V8 优化了它们的环境(闭包)记录并 gc 这些变量。 IE。这实际上根本不是问题(除了存在本地评估等的潜在病理情况外,我不记得它是如何处理的,但这几乎从来没有)。
    【解决方案2】:
    1. 您可以使用 node-inspector 通过 Chrome 开发工具调试节点应用程序。

    2. 关于未使用的闭包变量的公认答案是错误的,因为在现代 JS 引擎中,只有那些在内部函数中实际引用的变量才包含在闭包范围内。其余的会自动进行垃圾收集。换句话说,这个理论上的条件永远不会在 Node 中真正发生。

    3. 对于使用 express 的真实(且相当常见)示例,您可以创建中间件,为每个请求将文件加载到内存中,然后在同一请求中抛出未处理的异常,捕获抛出的异常,然后失败退出进程。

    抛出的异常会导致加载的请求资源在请求/响应周期结束时滞留而不是被清理。

    当异常发生时无法退出进程意味着Node不会关闭并被PM2或Forever之类的东西重新启动,而是会忽略错误并继续处理新请求,就像什么都没发生一样。由于资源没有被清理,进程会随着时间的推移消耗越来越多的内存,直到它拖累机器的性能并最​​终耗尽空间来分配新的资源。

    这显然会对用户体验产生负面影响。

    另见Why Would an Exception Cause Resource Leaks in Node.js

    【讨论】:

      【解决方案3】:

      发件人:http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml?showone=Closures#Closures

      但是要记住的一点是,闭包会保留指向其封闭范围的指针。因此,将闭包附加到 DOM 元素会创建循环引用,从而导致内存泄漏。例如,在以下代码中:

      function foo(element, a, b) {
        element.onclick = function() { /* uses a and b */ };
      }
      

      函数闭包保持对元素、a 和 b 的引用,即使它从不使用元素。由于 element 还保留对闭包的引用,因此我们有一个不会被垃圾收集清理的循环。在这些情况下,代码的结构可以如下:

      function foo(element, a, b) {
        element.onclick = bar(a, b);
      }
      
      function bar(a, b) {
        return function() { /* uses a and b */ }
      }
      

      【讨论】:

      • 有点帮助,但我对 DOM 元素不感兴趣,因为这是 Node.JS
      猜你喜欢
      • 2021-08-02
      • 1970-01-01
      • 1970-01-01
      • 2013-07-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多