【问题标题】:How to Garbage Collect an external Javascript load?如何垃圾收集外部 Javascript 负载?
【发布时间】:2019-11-15 13:57:26
【问题描述】:

给定一个加载器:

function loader(src, callback, fail) {
    let s = document.head.appendChild(document.createElement('script'));
    s.type = "text/javascript";
    s.src = src;
    s.onload = function() {
        callback()
        s.onload = null; //useful?
        s = null; //or maybe this?
    }
    s.onerror = fail
}

有一行s.onload = null 是否受益于 GC 释放一些内存?

【问题讨论】:

  • 在这种情况下,s(或this)到底是什么?通常整个对象无论如何都可以被收集。
  • delete frag 永远不会做任何事情——delete 会删除属性。开启严格模式。另外,我很确定 Bergi 从来没有接近过推荐它……
  • 开启严格模式会告诉你delete frag没有意义。我认为脚本也没有onreadystatechange
  • 为什么要围绕脚本创建一个片段包装器,这不是您要附加的唯一元素吗?
  • 这是一个最小化回流的 DocumentFragment,现已从帖子中删除。 developer.mozilla.org/en-US/docs/Web/API/DocumentFragment

标签: javascript performance garbage-collection dynamic-script-loading


【解决方案1】:

是的,s.onload = null 很有用,会进行垃圾收集!

As of 2019, it is not possible to explicitly or programmatically trigger garbage collection in JavaScript。这意味着它会在需要时收集。
虽然 there is cases where 设置为 null 可能会更早地进行 GC(但不会触发它)。

截至 2012 年,所有现代浏览器都提供 mark-and-sweepgarbage-collector。
它的工作原理是可达性

从全局上下文中无法访问的每个对象都将被删除

mark-and-sweep 周期性地发现并删除一个对象,当保存它的每个变量返回、重新分配或设置为 null。此外,现在不需要对无法从任何变量访问的内容递归地设置 null - 它无论如何都是由 mark-and-sweep 收集的。

现在到有问题的代码...

不需要s = null,因为变量在函数返回时会被清除,闭包从调用堆栈中移除并且GC会处理s

但仍有对脚本 onload 属性的引用,因为 <script onload> 是 DOM 中 document.head 的子对象,可从 window 访问!

callback 的内容可能是可访问的,但在这里是没有问题的。

如果浏览器足够聪明,可以在内部设置s.onload = null 怎么办?我们先在第一个 sn-p 中将其注释掉,然后在第二个 sn-p 中取消注释来尝试它...

function fetch(src, callback, fail) {
    let s = document.head.appendChild(document.createElement('script'));
    s.type = "text/javascript";
    s.src = src;
    s.onload = function() {
        callback()
        //s.onload = null; //useful?
    }
    s.onerror = fail
}

fetch("https://stackoverflow.com", 
       () => {console.log("Execute onload");}, 
       () => {console.log("File not found");})

setTimeout(() => {
    console.log(document.head.lastChild.onload)
},1000)

找到的文件执行时出错,因为它不是 Javascript。
onload 已执行但未删除。代码出现在日志中!
证明应该使用s.onload = null这一行,像这样:

function fetch(src, callback, fail) {
    let s = document.head.appendChild(document.createElement('script'));
    s.type = "text/javascript";
    s.src = src;
    s.onload = function() {
        callback()
        s.onload = null; //useful!
    }
    s.onerror = fail
}

fetch("https://stackoverflow.com", 
       () => {console.log("Execute onload");}, 
       () => {console.log("File not found");})

setTimeout(() => {
    console.log(document.head.lastChild.onload)
},1000)

【讨论】:

  • "s.onload 无法访问,因为它仅在脚本执行后直接调用一次使用" - 不。被使用和被访问彼此无关。你不想再次使用它,对,但 DOM 仍然引用它,这就是你应该明确清除它的原因。
  • 为了安全起见,这是有道理的。如何清除它? @Bergi:DOM 仍然引用它 - 在哪种情况下它引用?我希望你能区分 onload-codescript-code
  • 您通过s.onload = null 和/或s.remove() 清除它。 document.head 引用它的 s 子元素,它通过 .onload 属性引用处理函数。
【解决方案2】:

是的,将属性设置为null 确实有利于 GC。这样做会删除元素(包含在 DOM 中)对处理函数的引用,并且考虑到它可能是对函数的唯一引用,它使函数符合收集条件。但是,除非函数是对保留大块内存的变量的闭包,否则这不太可能产生很大影响。

您可能还想在回调中从 DOM 中删除 s 元素,使其也可被垃圾回收。

【讨论】:

  • 注意s.onload = null 可以替换为s = null?我编辑了我的帖子,以便您看到s 的范围。它不是重新分配s。对于每个fetch,它是一个新的s。现在您看到并且能够改进您的答案。我们现在可以用 remove the s 更准确地说s = null吗?
  • 不,s = null 毫无意义,因为无论如何都不会保留s 的范围,当fetch 函数结束时,该变量将被删除(假设没有关闭它)。所以我们忽略了对 script 元素的引用。但是为了使东西符合垃圾回收的条件,我们需要删除对值的 all 引用。对 script 元素最重要的引用是它包含在 DOM 中——页面上的任何代码都可以通过 DOM API 访问 script 对象并调用其onload 方法。 是我们需要清除的参考。
  • 根据您的说法,最好不要有任何return s,以确保不保留关闭并可以收藏。或者让返回值变为 void(并随之关闭)。是的,s = null 就像你说的那样毫无意义。正如我在回答中发现的那样,也许任何尝试帮助 GC 的方法都毫无意义?
  • @PauliSudarshanTerho 只要调用代码不将值存储在某处,return s 就无关紧要。不,这与关闭无关。
猜你喜欢
  • 2018-12-30
  • 2012-02-09
  • 1970-01-01
  • 1970-01-01
  • 2012-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-28
相关资源
最近更新 更多