当前位置: 首页 > 知识库问答 >
问题:

如何垃圾收集JavaScript闭包

范京
2023-03-14

我记录了以下Chrome bug,它导致了我的代码中许多严重的、不明显的内存泄漏:

(这些结果使用Chrome Dev Tools的内存探查器,它运行GC,然后获取所有未加载收集的内容的堆快照。)

在下面的代码中,someClass实例被垃圾收集(良好):

var someClass = function() {};

function f() {
  var some = new someClass();
  return function() {};
}

window.f_ = f();

但在这种情况下不会被垃圾收集(bad):

var someClass = function() {};

function f() {
  var some = new someClass();
  function unreachable() { some; }
  return function() {};
}

window.f_ = f();

以及对应的截图:

似乎一个闭包(在本例中为function(){})会使所有对象保持“活动”,如果该对象被同一上下文中的任何其他闭包引用,不管该闭包本身是否可达。

我的问题是关于其他浏览器(IE9+和Firefox)中闭包的垃圾收集。我非常熟悉WebKit的工具,比如JavaScript堆探查器,但是我对其他浏览器的工具知之甚少,所以我还没能对其进行测试。

在这三种情况下,IE9+和Firefox将垃圾收集someClass实例?

共有1个答案

太叔栋
2023-03-14

据我所知,这不是一个bug而是预期的行为。

从Mozilla的内存管理页面中可以看到:“截至2012年,所有的现代浏览器都提供了一个标记和扫描垃圾收集器。”“限制:需要使对象显式不可达”。

在失败的示例中,some仍然可以在闭包中访问。我尝试了两种方法来使它无法到达,但两种方法都奏效了。当您不再需要some=null时,您要么设置window.f_=null;然后它就会消失。

更新

我已经在Windows上的Chrome30、FF25、Opera12和IE10中试用过了。

该标准没有说任何关于垃圾收集的内容,但给出了一些应该发生的事情的线索。

  • 第13节函数定义,步骤4:“让闭包是创建13.2中指定的新函数对象的结果”
  • 第13.2节“scope指定的词汇环境”(scope=闭包)
  • 第10.2节词汇环境:

“一个(内部)词汇环境的外部指称是对逻辑上围绕着内部词汇环境的词汇环境的指称。

当然,一个外部词汇环境可能有它自己的外部词汇环境。一个词汇环境可以作为多个词汇内部环境的外部环境。例如,如果一个函数声明包含两个嵌套的函数声明,那么每个嵌套函数的词汇环境都将以当前执行周围函数的词汇环境作为其外部词汇环境。“

因此,函数将可以访问父函数的环境。

因此,some应该在返回函数的闭包中可用。

那为什么它不总是可用的?

看起来Chrome和FF足够聪明,在某些情况下可以消除该变量,但在Opera和IE中,some变量在闭包中可用(注意:要查看这一点,请在返回null上设置一个断点,并检查调试器)。

可以改进GC来检测函数中是否使用了some,但这会比较复杂。

一个坏例子:

var someClass = function() {};

function f() {
  var some = new someClass();
  return function(code) {
    console.log(eval(code));
  };
}

window.f_ = f();
window.f_('some');

在上面的示例中,GC无法知道变量是否被使用(代码已测试并在Chrome30、FF25、Opera12和IE10中工作)。

如果通过为window.f_分配另一个值而中断了对对象的引用,则释放内存。

在我看来这不是一个窃听器。

 类似资料:
  • Kubernetes 垃圾收集器的角色是删除指定的对象,这些对象曾经有但以后不再拥有 Owner 了。 注意:垃圾收集是 beta 特性,在 Kubernetes 1.4 及以上版本默认启用。 Owner 和 Dependent 一些 Kubernetes 对象是其它一些的 Owner。例如,一个 ReplicaSet 是一组 Pod 的 Owner。具有 Owner 的对象被称为是 Owner

  • 问题内容: 我有一段代码可以在内存中加载很大的图像。所以打电话似乎是合理的事情 在加载图像之前。据我所知,它毫无问题。 昨天,我决定使用一个名为FindBugs的非常有用的软件来扫描您的代码并报告可能导致错误或通常不建议使用的策略的问题。问题是我提到的这段代码得到了报告。描述是这样的: …强迫垃圾收集;除了基准测试代码外,都非常可疑 并继续阐述: 代码显式调用垃圾回收。除了基准测试中的特定用途外,

  • 问题内容: 有人可以解释一下G1垃圾收集器的工作原理吗?我还无法在任何地方找到任何全面,易于理解的描述。 谢谢 问题答案: 收集器将堆分成固定大小的区域,并跟踪这些区域中的实时数据。它将一组指针(“记住的集”)保留在区域内和区域外。当认为有必要使用GC时,它将首先收集实时数据较少的区域(因此,“垃圾优先”)。通常,这意味着一步就可以收集整个区域:如果进入一个区域的指针数量为零,则无需对该区域进行标

  • 本文向大家介绍Java垃圾收集,包括了Java垃圾收集的使用技巧和注意事项,需要的朋友参考一下 示例 C ++方法-新增和删除 在像C ++这样的语言中,应用程序负责管理动态分配的内存所使用的内存。当使用new运算符在C ++堆中创建对象时,需要相应地使用delete运算符来处置该对象: 如果程序忘记了delete一个对象而只是“忘记”了该对象,则关联的内存将丢失给应用程序。这种情况的术语是内存泄

  • JavaScript 具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。 而在C 和C++之类的语言中,开发人员的一项基本任务就是手工跟踪内存的使用情况,这是造成许多问题的一个根源。在编写JavaScript 程序时,开发人员不用再关心内存使用问题,所需内存的分配以及无用内存的回收完全实现了自动管理。这种垃圾收集机制的原理其实很简单:找出那些不再继续使用的变量,然后释放其

  • 问题内容: 我想向JVM注册一个回调,所以我知道何时进行垃圾回收。有什么办法吗? 编辑:我想这样做,以便可以在应用程序日志中发生垃圾收集时注销,这样我就可以查看它是否与我所看到的问题相关。启用- Xloggc很有帮助,但是将GC日志中的时间(自应用程序启动以来使用秒数)整合到我的主应用程序日志中有点棘手。 编辑2012年4月:从Java7u4开始,您可以从GarbageCollectorMXBea