清理不再引用且从未添加到文档中的元素

Clean-up of elements that are no longer referenced, and were never added to the document

本文关键字:文档 元素 添加 不再 引用      更新时间:2023-09-26

假设我创建了一个新元素:

let canvas = document.createElement('canvas');

现在,在脚本的后面,我删除了所有JS对它的引用。

canvas = null;

canvas 元素本身是否仍然存在,占用内存?还是会像其他未引用对象一样被垃圾收集?注意,我实际上并没有将它添加到文档中。

<canvas>元素本身是否仍然存在,占用内存?还是会像其他未引用对象一样被垃圾收集?

是的,它暂时还存在。是的,垃圾将在适当的时候被回收。

其他评论者似乎对canvas 变量<canvas> 元素之间的GC行为差异有点困惑。变量是在堆栈上分配的,而不是在堆上。只要它们在作用域中,它们就会占用堆栈上的少量内存。它们由于在调用链中而保持在作用域中。当函数退出并弹出堆栈帧时,它们使用的内存将被释放。

元素和其他对象一样,在堆上分配,并接受垃圾收集。当它们不再被引用时,它们将被GC化。可以使<canvas>元素不再被引用,方法是将任何引用它的变量设置为null或其他变量,(唯一)引用它的变量超出作用域。

当然还有另一种情况与变量的内存管理有关,那就是闭包。只要被封闭的函数"在作用域中"(换句话说,有东西引用它),被封闭的变量就会继续占用(少量)内存。这样一个变量的值——无论是DOM元素还是JS对象或其他任何东西——在闭包中的函数超出作用域之前不会也不能被GC。小例子:

function a() {
  const div = document.createElement('div');
  return function() {
    console.log(div);
  };
}
function b() {
  const func = a();
}

当输入b时,在堆栈上为func分配存储空间。调用a,它创建DOM元素并返回内部函数。此时,div仍然被分配,因为它已被关闭,并从内部函数中引用。DOM元素保留在堆中。一旦b退出,变量func就会从堆栈帧中弹出,这意味着不再有任何东西指向闭包函数。这意味着div不再在范围内。反过来,这意味着该元素不再被引用,并将被GC(最终)。

底线是你不需要担心这些。它只是工作,除非病理情况或发动机故障。

您可以在DevToolsProfiles选项卡上使用Take Heap Shot, Record Allocation Time, Record Allocation Profile来确定变量的内存状态。

  • 如何检测在JavaScript触发垃圾收集的内存分配?
  • <
  • 记忆术语/gh>
  • 如何记录堆快照

    发现DOM泄漏

    堆分析器具有双向反射的能力浏览器本地对象(DOM节点、CSS规则)之间的依赖关系JavaScript对象。这有助于发现其他不可见的漏洞发生的原因是被遗忘的分离的DOM子树浮动。

    DOM泄漏可能比您想象的要大。考虑下面的示例—#tree GC是什么时候?

      var select = document.querySelector;
      var treeRef = select("#tree");
      var leafRef = select("#leaf");
      var body = select("body");
      body.removeChild(treeRef);
      //#tree can't be GC yet due to treeRef
      treeRef = null;
      //#tree can't be GC yet due to indirect
      //reference from leafRef
      leafRef = null;
      //#NOW can be #tree GC
    

    #leaf维护对它的父节点(parentNode)的引用,并递归地一直到#tree,所以只有当leafRef被取消时,才会被取消#tree下的整棵树是GC的候选。

  • 内存管理

如果没有其他对canvas的引用,它应该被垃圾收集。

首先,有一个很大的警告,即不是每个实现都使用相同的垃圾收集算法,因为它还没有标准化。即旧版本的IE。

引用MDN文档:

众所周知,Internet Explorer 6和7存在引用计数垃圾DOM对象的收集器

然而,大多数现代浏览器使用标记-清除垃圾收集:

截至2012年,所有现代浏览器都提供了标记和清除功能垃圾收集器。JavaScript领域的所有改进垃圾收集(分代/增量/并发/并行)垃圾收集)在过去的几年里都实现了改进了这个算法,但没有改进垃圾集合算法本身也不是其约简的定义当"一个对象不再需要"时。

标记-扫描将在不可达时删除对象。因此,在您的示例中,如果为canvas变量赋一个新值,则新创建的元素将不可达,并被标记为垃圾收集。它可能会占用一小段时间的内存,直到垃圾收集器运行。另外,由于您使用了let,一旦内存中不再需要它所在的块作用域,它可能会被垃圾收集。

好吧,我现在可以给你一个有价值的答案,因为我已经亲自检查过了。在您的示例中,您刚刚创建了一个新变量并将其值设置为document.createElement('canvas')

在决定将该变量追加到DOM之前,它只不过是存储在变量中的一个值。通过将值设置为null,变量现在被存储,并将内存作为一个普通的、单个的空变量。

如果你把它附加到你的文档中,情况就完全相反了。如果将变量的值设置为null,元素就不会被删除,否则会浪费内存。