尝试协助JavaScript垃圾收集器是否有意义?

mar*_*ark 17 javascript garbage-collection

由于我更像是使用高级面向对象语言的JavaScript,我发现自己就像C/C++程序员一样,在我完成对象时就是这样.我知道GC最终会运行并清理我的烂摊子,但我能做些什么才能真正帮助它?

例如,我有一个大型/复杂主要对象的数组......每个主要对象可能包含数组和其他辅助对象引用.如果我已完成主要对象并将其从数组中删除,那么GC可能最终会找出对象所指向的所有其他内容,循环内部引用和所有内容.但是当从存储阵列中删除主要对象以通过它并且array.length = 0时,任何数组和reference = null任何对象基本上都可以使GC工作变得更容易(例如显式删除引用意味着GC更少)跟踪)?如果你愿意,那种手动析构函数.这样做是值得的还是我浪费时间/精力来获得很少/没有收获?

我想这更像是GC问题的一般理论(Java等),但我对这个问题的目的主要是对JavaScript感兴趣.

谢谢!

Sco*_*yet 5

很少.

这与永远不同.

通常,如果以合理的方式开发,未使用的对象将适当地超出范围,并且垃圾收集器将按预期工作.除非你真正理解你在做什么,否则帮助垃圾收集器完成工作的努力往往不能按预期工作.垃圾收集是一个由各种Javascript引擎的创建者进行了重大优化的区域.一般来说,除非:

  • 你已经用分析工具中的硬数据向自己证明了真正的需要,并且
  • 您可以展示对实际需要更改内容的真正理解,以帮助垃圾收集器.

这些都不容易实现.

所以也许最好的答案是,除非你已经足够先进以理解异常,否则你可能不应该担心这一点.


Gar*_*het 5

这可能取决于特定的垃圾收集器,因此答案取决于您使用的 JavaScript 引擎。

首先,我会注意到最好的应用程序代码做两件事。它实现了其功能和性能的技术目标,并且具有适应变化的能力。额外的代码应该足以证明增加的复杂性是合理的。

话虽如此,根据谷歌关于 V8 的注释,这是 Chrome 使用的 JavaScript 引擎,

V8 回收被称为垃圾收集的过程中不再需要的对象使用的内存。为了确保快速的对象分配、短暂的垃圾收集暂停和无内存碎片,V8 使用了停止世界的、分代的、准确的垃圾收集器。这意味着 V8:

  • 在执行垃圾回收周期时停止程序执行。
  • 在大多数垃圾回收周期中只处理对象堆的一部分。这最大限度地减少了停止应用程序的影响。
  • 总是确切地知道所有对象和指针在内存中的位置。这避免了错误地将对象识别为可能导致内存泄漏的指针。

在 V8 中,对象堆被分为两部分:创建对象的新空间和在垃圾回收周期中幸存的对象被提升到的旧空间。如果一个对象在垃圾回收周期中移动,V8 会更新所有指向该对象的指针。

分代垃圾收集器倾向于在堆之间移动对象,其中所有活动对象都移动到目标堆中。任何没有移动到目的地的东西都被认为是垃圾。目前尚不清楚 V8 垃圾收集器如何识别活动对象,但我们可以查看其他一些 GC 实现以寻找线索。

作为记录良好的 GC 实现行为的示例,Java 的 Concurrent Mark-Sweep 收集器:

  1. 停止应用程序。
  2. 构建可从应用程序代码访问的对象列表。
  3. 恢复应用程序。同时,CMS 收集器运行一个“标记”阶段,在该阶段它将可传递的对象标记为“非垃圾”。由于这与程序执行并行,因此它还跟踪应用程序所做的引用更改。
  4. 停止应用程序。
  5. 运行第二个(“备注”)阶段以标记新可达的对象。
  6. 恢复应用程序。同时,它“清除”所有标识为垃圾的对象并回收堆块。

它基本上是一个图遍历,从一组特定的节点开始。由于无法访问断开连接的对象,因此它们与其他断开连接的对象的连接不应该发挥作用。

http://www.oracle.com/technetwork/java/javase/memorymanagement-whitepaper-150215.pdf上有一份关于 Java 垃圾收集工作方式的不错的白皮书,虽然有些过时。垃圾收集并不是 Java 独有的,所以我怀疑 Java 虚拟机和其他运行时(如 JavaScript 引擎)采用的各种方法之间存在一些相似之处。

Raymond Chen 写了一篇博客文章,指出您即将释放的行走内存会对性能产生负面影响。上下文是在应用程序关闭时手动释放内存。由于块可能已经交换到磁盘,遍历引用的行为可能导致这些块被交换。在这种情况下,程序正在遍历刚刚标记为可用且未触及的块。

因此,在操作系统可能已经换出一些块的情况下,“协助”垃圾收集器的行为,特别是在寿命较长的对象上,最终可能会减慢速度。

而且,如果您没有生成足够的数据来担心换出,那么合理的垃圾收集器不会花费足够长的时间来注意到。

因此,这很可能不值得付出努力,而且可能适得其反。尽管删除对堆“支配者”的引用可能是有意义的。如果收集这些对象,将允许收集许多其他对象。因此,删除对集合本身的引用,而不是对集合中的每个项目的引用。