【问题标题】:Life of JavaScript objects & Memory LeaksJavaScript 对象的生命周期和内存泄漏
【发布时间】:2011-11-13 21:24:34
【问题描述】:

我对此进行了相当多的研究,但主要是通过将其他问题拼凑在一起,这仍然留下了一些疑问。在任何时候都不会刷新浏览器页面并且可能会存活很长一段时间(数小时)而不关闭的应用程序中(假设刷新页面或导航到另一个页面会重新启动 js 代码),确保对象的最佳方法是什么?释放并且没有内存泄漏。

这些是我关心的具体场景:

下面的所有代码都在一个显示模块模式中。

mycode = function(){}()

函数中的变量,我确定这个被 GC 收集就好了

function(){ var h = "ss";}

模块中的变量,如果不再需要 g = null 吗?

var g;
function(){ g = "dd";}

最后是 jqXHR 的生命:它返回后是否被清理干净?是否应该在所有情况下都将其设置为 null 作为预防措施,是否保留在函数或模块中?

如果这样做,x返回后是否被GC清理掉了?:

function(){
   var x = $.get();
   x.done = ...;
   x.fail = ...;
}

这样做的时候怎么样,x返回后也会被清理掉吗?:

var x;
function(){
   x = $.get();
   x.done = ...;
   x.fail = ...;
}

最后,有没有办法在不重启浏览器的情况下清理所有变量并重启模块?

【问题讨论】:

  • 如果您正在考虑内存泄漏,则不应考虑变量。考虑对象和引用以及后者对前者的可达性影响。
  • 是的,只是我将js中的所有内容都视为变量,但我明白你的意思。谢谢。

标签: javascript jquery memory-leaks


【解决方案1】:

函数中的变量,我确定这个被 GC 收集就好了

是的。

模块中的变量,如果不再需要 g = null 吗?

当然。

最后是 jqXHR 的生命:它返回后是否被清理干净?是否应该在所有情况下都将其设置为 null 作为预防措施,是否保留在函数或模块中?

各种浏览器都存在与 XHR 相关的错误,导致 onreadystatechange 及其关闭的任何内容都无法收集,除非开发人员小心地将其替换为虚拟值 (xhr.onreadystatechange = new Function('')),但我相信 jQuery 会为您处理这个问题.

最后,有没有办法在不重新启动浏览器的情况下清理所有变量并重新启动模块?

与页面关联的全局状态将占用浏览器内存,直到页面从浏览器历史堆栈中被逐出。 location.replace 可以在这里为您提供帮助,让您终止当前页面并将其替换为同一应用程序的新版本,而无需扩展历史堆栈。

将当前文档替换为提供的 URL 中的文档。与assign() 方法的区别在于,使用replace() 后,当前页面将不会保存在会话历史中,这意味着用户将无法使用返回按钮导航到该页面。

当您使用“模块”这个词时,它并不是一个对浏览器或其 JavaScript 解释器具有明确含义的术语,因此无法从内存中逐出模块而只能将一个模块逐出。有几件事情你需要担心,这可能会让人记忆犹新:

  1. 对已附加到 DOM 节点及其关闭的所有内容的 JavaScript 对象的引用 -- 事件处理程序是一个非常常见的示例。
  2. 实时 setIntervalsetTimeout 回调以及它们关闭的所有内容。
  3. 全局对象的属性及其关闭的所有内容。
  4. 如您所述,某些主机对象的属性,如 XHR 实例、Web Worker 回调等以及(您猜对了)它们关闭的所有内容。

任何要卸载一个模块并且只有一个模块的方案都需要处理所有这些,并确定哪些是模块的一部分,哪些不是。这是很多不同类型的清理。

【讨论】:

  • IYO,您是否建议 jqXHR 变量在不再需要时设置为空?我只是设置了一个jqXHR;得到我的回应,在它所在的 div 中加载了新的 html,我仍然可以访问该变量。虽然我很欣赏 GC 会做的事情(最终),但最好将它归零吗?还是让 js gc 做它的事?
  • @TonyBasallo,如果您需要注意内存使用情况,我会在您有疑问时使用null。它很便宜,当您不再需要东西时,它会在代码中明确显示,并且可能会导致错误代码快速失败。
【解决方案2】:

Javascript 是一种垃圾收集语言。它依靠垃圾收集器来清理未使用的内存。所以本质上,你必须相信 GC 会完成它的工作。

GC 将(最终,不一定立即)收集您无法访问的对象。如果您有一个对象的引用,那么它可能仍在使用中,因此 GC 不会触及它。

如果你没有直接或间接地引用该对象,那么 GC 就知道该对象不可能被使用,并且可以收集该对象。因此,您真正要做的就是确保重置对该对象的所有引用。

但是,GC 不保证 何时 对象将被收集。而且您不必担心这一点。

【讨论】:

    【解决方案3】:

    真正需要担心的唯一泄漏是闭包。

    function foo(a){
        var b = 10 + a;
        return function(c){
            return b + c;
        }
    }
    
    var bar = foo(20);
    var baz = bar(5);
    

    GC 无法删除var b - 它超出了范围。这是 IE 的一个大问题,对于 Mozilla 和 Chrome 来说更是如此。

    【讨论】:

    • 当然,这不是永久性泄漏。一旦返回函数被垃圾回收,那么b 也将被垃圾回收。
    • 如果我没记错的话,b 会在没有对返回函数的引用时进行 GC,因为这是在 foo 返回后唯一引用 b 的地方。
    • 只有一个?在撰写本文时,IE9 在将属性 innerHTML 设置为 HTML 字符串时会泄漏内存!
    • 但是什么开发者不会这样做呢?有时我们不得不硬着头皮接受浏览器糟糕透顶,整个互联网体验对用户来说很糟糕,所以我们无能为力 - 因此不用担心。
    【解决方案4】:

    作为任何垃圾收集语言的经验法则(例如,这适用于 Java、.NET 和 JavaScript),您要做的是确保没有对您想要的内存块的延迟引用让 GC 为您清理。当GC查看一块内存,发现程序中还有东西在引用它时,就会避免释放它。

    关于 jqXHR,在 AJAX 函数调用结束时将它们设置为 null 是没有意义的。一旦 GC 函数返回,AJAX 成功/错误/完成的所有参数都将被释放,除非 jQuery 正在做一些奇怪的事情,比如保持对它们的引用。

    【讨论】:

      【解决方案5】:

      GC 可以收集您无法再访问的每个变量。如果在函数中声明变量,一旦函数退出,变量可以被删除。 ,当计算机内存不足时,或在任何其他时间。

      当您执行 XHR 等异步函数时,这会变得更加复杂。 donefail 闭包可以访问在外部函数中声明的所有变量。因此,只要 donefail 可以执行,所有变量都必须保留在内存中。请求完成后,就可以释放变量了。

      无论如何,您应该确保每个变量都声明得尽可能深。

      【讨论】:

      • 一旦函数退出,不能保证这样的变量删除。它只能可以从那个点被移除。
      • 好的,我已经更正了。提问者要求内存泄漏,如果无法收集变量,就会出现。
      • @pimvdb,我同意iscan,但是激活帧是对象,如果存在任何实时闭包的范围,它们和它们包含的封闭属性都不是可收集的. ES5 中对eval 语义的更改允许闭包引用激活框架的子集——一些属性而不是其他属性——但局部变量值并不总是可以收集,因为它们出现的激活框架已经退出。
      【解决方案6】:

      是第一个带有'g'的例子,g应该设置为null。否则它将保留指向“dd”的指针。

      在第二个例子中,第一种情况下的“x”不需要设置为 null,因为当周围的函数退出时,该变量将“消失”。在第二种情况下,在函数外部使用 'x' 时,'x' 将保留分配给它的任何内容,并且在 'x' 设置为 null 或其他内容之前不会被 GC'd。

      【讨论】:

      • "dd" 是一个原始字符串,只是那个值本身;不是 JavaScript 中的“指针”。
      • @pimvdb,取决于解释器如何池化函数体中出现的字符串字面量,在收集函数function () { g = "dd"; } 之后,浏览器可能能够摆脱用于存储"dd" 的内存.
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-22
      • 2013-08-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-31
      相关资源
      最近更新 更多