【问题标题】:Why this code is consuming memory and it is not get fully reclaimed?为什么这段代码会消耗内存并且没有被完全回收?
【发布时间】:2011-04-14 06:56:18
【问题描述】:

我有一个简单的 JavaScript 代码,它在无限运行时会消耗内存。内存消耗由 Google Chrome 内部内存分析器监控。

  setInterval(function(){
     var xhr = new XMLHttpRequest();
     xhr.open('GET', 'json.txt', true);
     xhr.onreadystatechange = function() {
        if(this.readyState == 4 && this.status == 200) {
           console.log(this.responseText);
        }
     };
     xhr.send('');
  }, 500);

带有上述代码示例的 HTML 文件和 json.txt 托管在我的本地服务器上,获取文件的时间不超过 500 毫秒(通常约为 7-10 毫秒)。

长时间运行内存图是这样的

编辑工作一小时后的同一个 Chrome 窗口

编辑 从长远来看(小时),并非所有内存都被回收,图表仍在上升。 我了解为什么内存被消耗,我不明白为什么它没有完全回收

编辑 这就是我可以减少内存泄漏的方法

  var callback = function(){
      if(this.readyState == 4 && this.status == 200) {
          console.log(this.responseText);
      }
  } 

  setInterval(function(){
     var xhr = new XMLHttpRequest();
     xhr.open('GET', 'json.txt', true);
     xhr.onreadystatechange = callback;
     xhr.send('');
  }, 500);

这项改进允许不将回调的闭包链接到xhr var。

【问题讨论】:

  • 你能用接受的答案修复你的泄漏吗?
  • @brainjam,接受的答案给了我线索,如何改进这段代码。查看我的编辑。
  • 我的大脑很痛。我猜您最初的构造是在匿名函数和闭包之间创建循环引用。这是一个非常微妙的(无论如何对我来说)内存泄漏模式的很好的例子。感谢您的坚持,直到您得到一个好的答案。

标签: javascript garbage-collection xmlhttprequest


【解决方案1】:

对于一个你反复加载json.txt 的内容,这将需要一些内存。除此之外,XHR 本身可能还需要一些内存。不过,我认为这不是什么大问题,因为无论如何内存似乎都被回收了。

【讨论】:

  • 从长远来看,我可以在图表上看到,并不是所有的内存都被回收了。
  • 编辑了我的问题。经过一个小时的工作,添加了同一 Chrome 窗口的另一张图表。
【解决方案2】:

我想这个间隔并不是在真空中设定的。你传递给 setInterval 的函数可以访问它定义的词法范围。这意味着它周围的每个变量在垃圾收集器的眼中总是可以访问的,即使你没有在函数中使用这些变量。垃圾收集器永远不会清理它。在更卫生的环境中定义函数可能会更好,因为它不会对任何大型变量进行闭包访问。

编辑:抱歉,我会尽量让自己更清楚。这与 XHR 无关——你可以在 setInterval 函数中放置任何东西(甚至什么都没有),如果在它的词法范围内有一些你想要清理的变量,它仍然会泄漏。您只需要确保 setInterval 函数没有对您要清理的任何变量的闭包访问权限。例如:

(function() {
  var a = readFile('a-very-big-file.txt');

  setInterval(function() {
    console.log('im leaking!');
  }, 500);
})();

那会泄漏。即使 setInterval 没有做任何特别有意义的事情,它仍然可以访问“a”,并且垃圾收集器认为它是可访问的。

【讨论】:

  • 好的,setInterval 中的函数至少可以访问全局window 对象。但它只声明一次,不能无限消耗内存。好吧,每次调用该函数时,都会创建新的 XHR 对象(我们将其命名为 A),并将新函数 (B) 设置为其属性之一。现在 fn B 看到对象 A。但是这些对象无法从任何其他范围访问,因此就我了解 GC 的工作而言,必须进行垃圾收集。
  • 这绝不能“增量”泄漏。此处没有在循环中创建新对象。
【解决方案3】:

为什么不应该呢?至少 var xhr = new XMLHttpRequest(); 必须在每次迭代时消耗内存并由 GC 收集(可能在您的图表上可以看到)。

【讨论】:

  • 添加了另一个图表。在那里可以看到不是所有的内存都被回收了。
【解决方案4】:

如果请求-响应周期比指定的时间间隔长,肯定会有一些内存开销。

因为只有在请求完成(成功或错误)后才会释放内存。

【讨论】:

    【解决方案5】:

    内存没有被完全回收很可能是由于使用了 console.log。我假设在后台,控制台会将内容添加到内部数组,或者可能是一个字符串,并且由于您正在向其中添加大块数据,并且永远不会从中删除,GC 永远不会清理它,而应用程序是跑步。尝试注释掉该语句,看看它是否会改变内存使用情况。用于创建 XMLHttpRequest 的内存最终会被 GC 释放,因为在请求完成后对它的引用将成为孤立的,但 console 是一个全局变量,当页面可用时它仍然保留在范围内。

    【讨论】:

    • 不,这不是原因。我已经删除了console.log(...) 语句,它仍然在消耗内存。半小时内谷歌浏览器标签消耗的内存(根据about:memory,私有内存参数)从6288Kb上升到22304Kb
    • 我在节点中遇到了同样的情况.. Console.log 消耗内存.. 但一段时间后它被释放了..
    猜你喜欢
    • 2023-04-06
    • 1970-01-01
    • 1970-01-01
    • 2019-09-30
    • 2018-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-21
    相关资源
    最近更新 更多