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

时间:2016-10-12 19:01:29

标签: javascript dom

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

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

现在,稍后在脚本中,我删除了对它的任何JS引用。

canvas = null;

画布元素本身是否仍然存在,占用内存?或者它会像任何其他未引用的对象一样被垃圾收集?请注意,我还没有将其添加到文档中。

4 个答案:

答案 0 :(得分:5)

  

<canvas>元素本身是否仍然存在,记忆?或者它会像其他任何未引用的对象一样被垃圾收集吗?

是的,它暂时仍然存在。是的,它将在适当的时候进行垃圾收集。

其他海报似乎对canvas 变量<canvas> 元素之间GC行为的差异略有混淆。变量在堆栈上分配,而不是堆。只要它们在范围内,它们就会占用堆栈上的少量内存。由于处于调用链中,它们仍然在范围内。退出函数并弹出堆栈帧时,将释放它们使用的内存。

元素与其他对象一样,在堆上分配,并进行垃圾回收。当不再引用时,它们是GC'd。通过将引用它的任何变量设置为<canvas>或其他内容,,可以使null元素不再被引用 only)变量指的是超出范围。

当然还有一个与变量的内存管理有关的案例,即闭包。只要封闭的函数是“在范围内”,换句话说,某个引用它的东西,一个被关闭的变量继续占用(少量)内存。这个变量的值 - 无论是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(最终)。

底线是您无需担心任何此类问题。它只是有效,除非是病理情况或引擎错误。

答案 1 :(得分:2)

您可以在Take Heap Shot的{​​{1}}标签处使用Record Allocation TimeRecord Allocation ProfileProfiles来确定变量的内存状态。

  • How to detect the memory allocations that are triggering garbage collection in JavaScript?
  • Memory Terminology
  • How to Record Heap Snapshots

      

    揭露DOM泄漏

         

    堆分析器具有反映双向的能力   浏览器本机对象(DOM节点,CSS规则)和之间的依赖关系   JavaScript对象。这有助于发现其他不可见的泄漏   由于被遗忘的独立DOM子树浮动而发生。

         

    DOM泄漏可能比您想象的要大。请考虑以下示例 - 何时是#tree GC?

    DevTools
         

    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 维护对它的父(parentNode)的引用,并递归到#leaf,所以只有当leafRef无效时才是   #tree下的整个树,GC的候选者。

  • Memory Management

如果没有对#tree的其他引用,则应该进行垃圾回收。您可以通过检查堆快照来确定这一点。

答案 2 :(得分:2)

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

引用MDN documentation

  

已知Internet Explorer 6和7具有引用计数垃圾   DOM对象的收集器

但是,大多数现代浏览器都使用标记和扫描垃圾回收:

  

截至2012年,所有现代浏览器都进行了标记和扫描   垃圾收集器即可。 JavaScript领域的所有改进   垃圾收集(世代/增量/并发/并行)   垃圾收集)在过去几年是实施   这种算法的改进,但没有改进垃圾   收集算法本身也没有减少其定义   当“不再需要一个物体”时。

Mark-and-Sweep会在无法访问时移除对象。因此,在您的情况下,如果为canvas变量分配新值,则新创建的元素将无法访问并标记为垃圾回收。它可能占用内存很短的时间,直到垃圾收集器运行。此外,由于您使用了let,因此只要内存中不再需要block scope it's located in,就可能会对其进行垃圾回收。

答案 3 :(得分:1)

哎呀,我现在可以给你一个有价值的答案,因为我已经自己检查过了。在您的情况下,您刚刚创建了一个新变量并将其值设置为document.createElement('canvas')

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

如果您将其附加到您的文档中,情况将完全相反。通过将变量的值设置为null,该元素不会被删除,然后会浪费内存。