【问题标题】:Memory leak in javascript, functions inside loopjavascript中的内存泄漏,循环内的函数
【发布时间】:2018-07-03 10:58:57
【问题描述】:

我正在循环从 ajax 响应中获得的响应,该响应是一个包含 1000 个对象的数组,该响应用于创建一个包含 1000 行的 html 表:

第一种情况:

    for (var i in msg.myObjects) {
    $('#mytablebody').append('<tr><td>' + msg.myObjects['item1'] + '</td><td>' + 
msg.myObjects['item2'] + '</td><td>' + msg.myObjects['item3'] + '</td><td>' + 
msg.myObjects['item4'] + '</td><td>' + msg.myObjects['item5'] + '</td><td>' + 
msg.myObjects['item6'] + '</td><td>' + msg.myObjects['item7'] + '</td> .... </tr>');
    }

结果 => 内存泄漏我的 RAM 去了 2Go,我的浏览器崩溃了

第二种情况:

    for (var i in msg.myObjects) {
        document.getElementById('mytablebody').innerHTML = document.getElementById('mytablebody').innerHTML + '<tr><td>' + 
msg.myObjects['item1'] + '</td><td>' + 
    msg.myObjects['item2'] + '</td><td>' + msg.myObjects['item3'] + '</td><td>' + 
    msg.myObjects['item4'] + '</td><td>' + msg.myObjects['item5'] + '</td><td>' + 
    msg.myObjects['item6'] + '</td><td>' + msg.myObjects['item7'] + '</td> .... </tr>';
        }

结果 => 内存泄漏,我的 RAM 达到 800Mo,我的浏览器因第二个 ajax 调用而崩溃

第三种情况:

var stringResponse = '';
        for (var i in msg.myObjects) {
            stringResponse += '<tr><td>' + msg.myObjects['item1'] + '</td><td>' + 
        msg.myObjects['item2'] + '</td><td>' + msg.myObjects['item3'] + '</td><td>' + 
        msg.myObjects['item4'] + '</td><td>' + msg.myObjects['item5'] + '</td><td>' + 
        msg.myObjects['item6'] + '</td><td>' + msg.myObjects['item7'] + '</td> .... </tr>';
            }
document.getElementById('mytablebody').innerHTML = stringResponse 

结果 => 没有内存泄漏

好的,直到这里我得出结论,首先,.append() 会导致内存泄漏,其次,你不应该在循环中使用 DOM 元素。但是当我做第 4 个场景时,我得出的结论是第一个结论是错误的(不完全正确),而第二个结论仍然是正确的。

第四种情况:

    var stringResponse = '';
            for (var i in msg.myObjects) {
                stringResponse += '<tr><td>' + replaceNulls(msg.myObjects['item1']) + '</td><td>' + 
            msg.myObjects['item2'] + '</td><td>' + msg.myObjects['item3'] + '</td><td>' + 
            msg.myObjects['item4'] + '</td><td>' + msg.myObjects['item5'] + '</td><td>' + 
            msg.myObjects['item6'] + '</td><td>' + msg.myObjects['item7'] + '</td> .... </tr>';
                }
    document.getElementById('mytablebody').innerHTML = stringResponse 

function replaceNulls(input) {
  return input != null ? input : ''
}

结果 => 内存泄漏我的 RAM 去了 2Go,我的浏览器崩溃了

我的问题是:

当我们调用发生在循环之外的函数时,可能会导致内存泄漏,为什么?

如何避免这种情况(不删除函数或将其处理移至循环内部)?

【问题讨论】:

  • 尝试在数组中推送html 字符串,并在将其分配给innerHTML 时使用Array.join() 方法
  • 问题仅仅是因为你在一个循环中进行了数千个 DOM 操作,速度非常快。在不了解浏览器 JS 引擎内部结构的情况下,可以肯定地说这并不理想。这就是您创建的第三种方法(在内存中构建 HTML 字符串,然后将其附加到单个 DOM 操作中)性能最佳的原因。这是所有 DOM 附加应该完成的方式,但在大多数情况下,它们的规模很小,以至于不会注意到任何性能问题。
  • it may causes a memory leak, why ?这里没有内存泄漏..
  • @RoryMcCrossan 是的,我同意,但我现在卡在第 4 种情况下,我不能在循环中使用函数
  • 您遇到的行为很正常。您正在生成 1000 个 DOM 元素。我不知道您的具体项目需求是什么,但我建议您重新考虑您的 UI,方法是放置一个按钮来限制屏幕上的对象或使用延迟加载

标签: javascript jquery ajax memory memory-leaks


【解决方案1】:

现代网络浏览器的 1000 个表条目应该不会导致任何问题。

在这里,我将 10,000 个项目添加到一个没有 ram 问题的表中。 它快速的原因是因为我在一个分离的 DOM 元素中构建了列表,然后在完成后附加。

您的问题可能只是 DOM 绘制问题,浏览器必须重新绘制所有更新。

我注意到的另一件事,for (var i in msg.myObjects) 取决于您的 msg.myObjects 包含的内容,这不是一件好事。如果你可以使用现代 JS,。 for (const i of msg.myObjects) 更好。

var table = document.querySelector("table");

var tbody = document.createElement("tbody");

function addLine (txt) {
  var tr = document.createElement("tr");
  var td = document.createElement("td");
  tr.appendChild(td);
  td.innerText = txt;
  tbody.appendChild(tr);
}

for (var l = 1; l <= 10000; l += 1) {
  addLine("This is Line " + l + ", and some extra text");
}

table.appendChild(tbody);
<table>
  <thead><tr><th>Test</th></tr></thead>

</table>

【讨论】:

  • 让我测试一下你的想法,我会告诉你结果,但我确信这会导致我的案例发生内存泄漏,因为我有 14 个充满数据的列
  • 我尝试在 jsfiddle 上创建 14 个节点并将它们附加到表格主体上,效果很好,我现在将在我的代码上对其进行测试,然后我会告诉你结果
  • why should i use const instead of using var 与其说是const,不如说是for in,它也会迭代没有拥有的属性,这就是你在for in 时看到很多obj.hasOwnProperty(prop) 的原因经常使用,for of 没有这个问题。 const 也是在现代 JS 引擎中使用的好习惯,用于性能和 linting。
  • 好的,现在我完全理解了“for in”和“for of”之间的区别,我尝试在这个范围之外创建一个数组原型,当我使用“for in”循环时,它会显示出来就像它是我数组上的一个元素一样,当我使用“for of”时,它没有显示出来。所以“for of”在这里更有用。 +1
  • 把它放在点击函数的开头会移除旧的 tbody,-> var otoby = table.querySelector("tbody");if (otoby) otoby.remove();
猜你喜欢
  • 1970-01-01
  • 2017-06-06
  • 2011-01-01
  • 2012-04-08
  • 2014-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-12
相关资源
最近更新 更多