我能否触发JavaScript的垃圾回收?

76

我想触发JavaScript垃圾回收。这是否可能?为什么我想要或不想要这样做?


7
不。[评论限制] - RobG
2
嗨,实际上在我的应用程序中,每20秒都会通过ajax调用传输大量数据。由于数据量过大,浏览器在几分钟后就会崩溃。因此,我想清除浏览器的内存以避免浏览器崩溃。 - Abhinav
2
你需要解决的问题是你的浏览器首先接收到太多的数据。 - BoltClock
8
BoltClock的评论很无用。在一个简单的“我的公司是这样的”主页上,这在某些情况下是正确的,但是当你开发一种“软件”,例如在高级股票图表中实时收集传感器统计数据时,拥有“庞大”的数据没有问题。 - prdatur
3
在开始某些类型的动画之前,触发GC的一个非常好的原因是我们不希望在动画过程中出现犹豫。请注意,这里的GC指垃圾回收机制。 - David Spector
显示剩余5条评论
9个回答

62

我出去小旅行了一下,想要找到你的一个问题的答案:这是可能的吗?

全城的人都在说删除引用就可以解决问题。有些人说擦除对象是额外的保证(example)。所以我写了一个脚本,尝试了书中的每个技巧,惊讶地发现在Chrome(22.0.1229.79)和IE(9.0.8112.16421)中,垃圾回收甚至似乎不起作用。Firefox(15.0.1)没有遇到任何主要缺点,除了一个(请参见下面的4f案例)。

在伪代码中,测试大致如下。

  1. 创建一个容器,一个数组,用于存储某种类型的对象。我们将在此称这个容器为 Bertil

  2. 每个元素都是一个对象,在 Bertil 中作为属性声明自己的数组容器。这个数组将保存很多字节。我们将 Bertil 的任何一个元素称为“对象”,Joshua。每个 Joshua 的字节数组将被称为 Smith

    这里有一个思维导图供您参考:

    Bertil [对象数组] -> Joshua [对象] -> Smith [字节数组] -> 未命名 [字节]。

  3. 当我们把可用内存搞乱时,请等待一两秒钟,然后执行以下任意一种“销毁算法”:

    4a. 在主对象容器 Bertil 上抛出一个删除操作数。

    4b. 在该容器中的每个对象上抛出一个删除操作数,杀死每个活着的 Joshua。

    4c. 在每个字节数组中抛出一个删除操作数,即 Smiths。

    4d. 将 NULL 分配给每个 Joshua。

    4e. 将 UNDEFINED 分配给每个 Joshua。

    4f. 手动删除 Joshua 持有的每个字节。

    4g. 以工作顺序执行所有上述操作。

那么发生了什么?4a4b的情况下,没有任何浏览器的垃圾回收器(GC)启动。在4c至4e的情况下,Firefox确实启动并显示了一些概念证明。内存在不到一分钟内被回收。由于某些变量的硬编码默认值作为测试配置,4f4e导致Chrome挂起,因此我无法得出任何结论。您可以自行使用自己的变量进行测试,链接将很快发布。IE在4f和4e中幸存了下来,但他的GC像往常一样死亡。出乎意料的是,Firefox幸存了下来,但未通过4f。Firefox幸存并通过了4g。

在所有浏览器的GC未能启动的情况下,等待至少10分钟并不能解决问题。重新加载整个页面会导致内存占用量翻倍。

我的结论是我在代码中一定犯了严重的错误,或者您的问题的答案是:不,我们不能触发GC。每当我们试图这样做时,我们将受到严厉的惩罚,并且应该把头埋进沙子里。请鼓励您自己前进,尝试这些测试用例。查看代码中的注释以获取详细信息。此外,下载页面并重新编写脚本,看看是否可以以更适当的方式触发GC。我肯定失败了,我无法相信Chrome和IE没有一个有效的垃圾回收器。

http://martinandersson.com/dev/gc_test/?case=1

http://martinandersson.com/dev/gc_test/?case=2

http://martinandersson.com/dev/gc_test/?case=3

http://martinandersson.com/dev/gc_test/?case=4

http://martinandersson.com/dev/gc_test/?case=5

http://martinandersson.com/dev/gc_test/?case=6

http://martinandersson.com/dev/gc_test/?case=7


2
我也注意到在V8引擎(Chrome)中删除大量对象是一个不好的想法。它非常缓慢,而将其分配为null则更快。 - jjrv
3
你说:“删除大量对象”。你可能需要知道,在 JavaScript 中,你无法删除一个对象(也不能删除函数或变量),只能删除对象属性和数组元素。当我们完成使用它们后,将 null(或 undefined?)分配给每个对象引用应该就足够了。从理论上讲,这就是我们需要做的。但是正如我的研究所表明的那样,我在 Chrome 和 IE 中激活 GC 方面失败得彻底。基本上,在具有自己垃圾回收器的托管代码中很难出现内存泄漏,但对我来说情况却相反。 - Martin Andersson
2
+1,可能是我在SO上读过的最好的答案。有趣(但不过分),恰到好处地写到了要点上。我正在构建真正动态的数据管理系统 - 可能会使用浏览器作为从Google电子表格等地方获取数据的桥梁。因此,“数据量太大”的问题也将成为我的问题... - Kallex
2
@MartinAndersson 我跑了你的测试。然后发现(也在下面的答案中提到),IE遵守对“CollectGarbage()”的显式调用(我通过测试将其添加到您的第2个模式中 - 没有进行彻底测试)。虽然这“不被推荐”,但等待时它释放了内存,而没有释放任何东西... - Kallex
1
我知道这是一个旧帖子,但我想为那些偶然发现它的人添加一些额外的上下文。在JS中调用delete应该尽量避免,除非绝对需要。为什么?因为它会破坏该对象的JS引擎优化,至少在V8中是如此...我不确定其他引擎是否也是如此。您可以通过使用分析器实时查看此情况。如果您在键上调用delete,则会在分析器中看到该对象现在被标记为“取消优化”。好文章:https://github.com/P0lip/v8-deoptimize-reasons - th317erd
显示剩余3条评论

9

您可以手动触发IE和Opera中的JavaScript垃圾回收器,但不建议这样做,最好完全不使用它。我提供更多的命令只是为了信息目的。

Internet Explorer:

window.CollectGarbage()

Opera 7+:

window.opera.collect()

7
自动垃圾回收是自动运行的。它何时运行并释放未被引用的对象完全取决于具体的实现方式。
如果你想要释放某个对象,只需要从你的JavaScript中清除对它的引用即可。垃圾回收器将会释放它。
如果你能解释一下为什么你认为你需要这样做或者想要这样做,并展示相关代码,我们可能能够帮助你解释你可以采取的替代方案。

3
你好,实际上在我的应用程序中,每隔20秒都会通过Ajax调用传输大量数据。由于数据过多,浏览器在几分钟后就会崩溃。因此,我想清除浏览器的内存以避免浏览器出现故障。 - Abhinav
1
代码非常简单,实际上我们通过实时数据生成动态图表,每20秒更新一次。问题已经解释过了。 - Abhinav
1
为了解决这个问题,我想在一段时间后清除浏览器缓存,我的问题是,是否可以通过脚本清除浏览器内存? - Abhinav
5
我已经回答过你了,在不看你的代码的情况下可以采取哪些措施。你不能自己调用垃圾回收器。如果你清除了对这个大数据的所有引用并让 JS 引擎有一些空闲周期,垃圾回收器会为你释放它。我只能通过查看你的具体代码来提供建议。 - jfriend00
3
清除浏览器内存的另一种方法是重新加载页面,而不仅仅使用ajax并停留在同一页上 - 尽管如果您的代码结构良好且已释放对不再使用的数据的引用,则不应该需要这样做。 - jfriend00
显示剩余3条评论

4
  1. 检查你的代码是否有全局变量。可能会有通过ajax调用传输的数据被存储,并在某个地方被引用,而你没有考虑到这一点。
  2. 解决方法是将大量数据处理包装在匿名函数调用中,并且只在此调用中使用局部变量,以防止在全局范围内引用数据。
  3. 或者,你可以将所有已使用的全局变量赋值为null。
  4. 还应该查看此问题。看一下答案中的第三个示例。你的大量数据对象可能仍然被异步调用闭包引用。

2

这个答案提供了以下垃圾回收请求代码,适用于基于Gecko的浏览器:

    window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
      .getInterface(Components.interfaces.nsIDOMWindowUtils)
      .garbageCollect();        

2
根据MDN的说明,garbageCollect函数“如果在非调试版本中没有UniversalXPConnect权限的情况下调用,则会引发DOM安全错误。在调试版本中可供所有调用者使用。”可能不适用于所有现代浏览器。被投票降级。 - David Spector

1

我看到了这个问题,决定分享一下我最近的发现。

我研究了在Chrome中正确处理WeakMap的方法,实际上看起来还不错:

1)var wm = new WeakMap()

2)var d = document.createElement('div')

3)wm.set(d, {})

此时WeakMap保存了该条目,因为d仍然引用该元素

4)d = null

此时没有任何东西引用该元素和它的弱引用对象,几分钟后该条目消失并被垃圾回收。

当我将元素添加到DOM中并执行相同的操作时,它没有被回收,这是正确的,因为它已从DOM中删除,并且仍在等待被回收 :)


“而且几分钟后,条目消失并进行了垃圾回收”,你是如何验证的? - Pacerier
好吧,那已经有一段时间了,但据我所记,我只是在 DevTools(堆快照)中检查了内存图。 - GullerYA
在普通的使用场景中,在几分钟后触发GC是没有用的,因为我们一般是在某些动画之前触发GC以避免动画卡顿。 - David Spector

1

是的,您可以通过重新加载页面来触发垃圾回收。

您可能希望考虑使用工厂模式来帮助重复使用对象,这将大大减少创建的对象数量。特别是,如果您不断地创建相同的对象。

如果您需要了解工厂模式,请获取Ross Harmes和Dustin Diaz撰写、APress出版的《Pro Javascript Design Patterns》一书。


45
太棒了!我刷新了页面,所有物体都不见了! - kroe
5
我认为这不是原问题的意图。刷新页面显然是清除所有内容的方法,但对于运行时间较长、逻辑复杂且垃圾回收工作敏感的单页应用程序来说,这不是一个解决方案。 - GullerYA
@Trevor,重新加载页面不仅会进行垃圾回收,还会进行非垃圾回收。不要把孩子和洗澡水一起倒掉! - Pacerier
刷新页面会在IE11中使我的RAM内存占用翻倍,非常令人沮丧。我必须打开一个新窗口并关闭旧窗口以释放已关闭的选项卡的内存。最大的问题是由于内存已经满了,我无法打开另一个窗口。 - Coty Embry

1
我正在阅读Trevor Prime的答案,它让我笑了一下,但后来意识到他说的有道理。
  1. 重新加载页面会进行“垃圾回收”。
  2. 使用location.reload()或其他方法刷新页面。
  3. 使用JSON.parse/stringify和localStorage.getItem/setItem来持久化所需数据。
  4. 使用iframe作为用户体验的页面重新加载方式。
你只需要在iframe中运行代码并在保存有用信息到localStorage的同时刷新iframe页面。您必须确保iframe与主页面在同一域上以访问其DOM。
您可以不使用iframe,但是用户体验无疑会受到影响,因为页面将可见重置。

1
我怀疑重新加载 iframe 不会有什么区别。你需要进行完整的顶部重新加载。 - Pacerier
重新加载在动画的常见用例中是无用的。动画应该随时可能,而不仅仅在页面加载时可见于用户。 - David Spector

0
如果像其他回答所暗示的那样,没有触发GC的方法,那么解决方案就是由适当的浏览器标准组向window或document添加一个新的JavaScript函数来执行此操作。允许Web页面在自己选择的时间触发GC是有用的,这样动画和其他高优先级操作(声音输出?)就不会被GC中断。
这可能是另一种“我们一直都是这样做的,不要搞破坏”的情况。
已添加:
MDN记录了一个名为“Components.utils.schedulePreciseGC”的函数,它允许页面在未来某个时间安排GC,并在GC完成时调用回调函数。 这在除Firefox之外的其他浏览器中可能不存在;文档不清楚。 在动画之前可能可以使用此函数;需要进行测试。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接