Catching memory leaks with Chrome DevTools

当分配的内存没有返回给操作系统或内存池时,我们将其称为内存泄漏。 在这种情况下,内存未被任何应用程序使用,并且被不必要地占用。 这会导致低性能、高延迟和频繁崩溃。

Understanding memory leaks

如果您熟悉 C 等低级语言,您一定使用过 malloc() 和 free()。 相比之下,JavaScript 在创建对象时自动分配内存,并在不再使用时释放它。

好吧,因为它是自动管理的,所以我们作为开发人员总是有一个错误的印象,即我们不需要担心浏览器中的内存管理。 如果一个站点使用越来越多的内存,这意味着没有人收集它并且存在内存泄漏。

Garbage collectors

如果垃圾收集器 (GC) 是完美的,那么内存泄漏就不是问题。 问题是他们的算法不够聪明,无法检测内存泄漏。 因此,需要人工干预。

垃圾收集器执行查找程序不再使用的内存并将其释放回操作系统以供将来重新分配的过程。 该方法有效,但仍然会发生内存泄漏。 该方法无法检测每个泄漏,例如泄漏的引用。

Why is there a memory leak?

下列是几种常见的内存泄漏类型。

Accidental global variables

function getWork() {
  this.work = “I am Memory leak”;
}
// The this here refers to window object and hence this variable will be created in the window.
getWork();

这里的 this 指的是 window 对象,因此这个变量将在 window 中创建。

由于全局变量不是由 GC 收集的,如果此字符串变得太大,可能会导致内存泄漏。 意外全局变量的一个类似示例是在不使用 let 和 var 关键字的情况下声明变量。

Detached DOM nodes

分离 DOM 节点是一个关键问题。 由于全局引用,分离的节点仍然存在于内存中。

var node = document.createElement(‘a’);
node.id = 'id1';
document.body.appendChild(node);
var main = {
   Id: document.getElementById(‘id1’)
}
function removeElement(){
   document.body.removeChild(document.getElementById(‘id1’));
}
removeElement();

在上面的例子中,removeChild 函数从树中移除了 DOM 节点,但是全局主对象中的引用 Id 仍然保留在内存中并且没有被垃圾收集。

闭包

闭包为内部函数维护外部函数变量的范围,即使在外部函数的范围之外。

function getScore(x) {
   function score(y) {
      return x + y;
    }
   return score;
}
var initial = getScore(2);
var final = initial(3);

这里的函数score,也就是内部函数,有一个全局引用,叫做initial。 这个初始引用永远不会被垃圾收集。

Tools to identify memory leaks

意外的全局变量 内存泄漏可以通过分析轻松检测到。 我们举一个代码片段的例子,它会因为全局变量而导致内存泄漏。
例子:

var x = []
var bool = false;
function grow(){
  x.push(new Array(100000).join(‘a’));
  if(bool){
    setTimeout(grow, 1000);
  }
}
function start(){
  grow();
  bool = true;
}
function stop(){
  bool = false;
}

到 Chrome 开发者工具里,打开 Profiles 标签页:

选择 Take Heap Snapshot.

在这里,window 对象的黄色实际上描绘了从 JS 代码中直接引用的节点。 我们需要修复这里的代码,以便我们可以摆脱黄色标记。

此处的选项是在函数内将数组设为局部,以便垃圾收集器可以收集它或显式删除全局变量。 您可以找到更正后的代码:

var bool = false;
function grow(){
  var x = [];
  x.push(new Array(100000).join(‘a’));

  if(bool){
   setTimeout(grow, 1000);
  }
}
function start(){
  grow();
  bool = true;
}
function stop(){
  bool = false;
}

Allocation profiler

Allocation Timeline 是另一个工具,可以帮助您跟踪 JS 堆中的内存泄漏。 要记录时间线,请转到您的 profile 面板,然后单击上面给出的相同代码的开始。

当我们单击如图所示的开始按钮并使用分配分析器进行配置时,我们可以看到它生成了如图所示的蓝线。

蓝条代表新的内存分配,这可能是内存泄漏。 您可以通过缩放这些蓝色条中的任何一个来查看详细信息。 此处的详细信息表示被推入数组且从不进行垃圾回收的长字符串。

更多Jerry的原创文章,尽在:"汪子熙":

03-05 20:21