这可能取决于具体的垃圾收集器,所以答案会根据您使用的JavaScript引擎而有所不同。
首先,我要注意到最好的应用程序代码要做两件事情。它要实现其功能和性能的技术目标,并且要对变化具有弹性。额外的代码应该有足够的目的来证明增加复杂性是合理的。
话虽如此,根据
Google关于V8的说明,这是Chrome使用的JavaScript引擎,
V8通过垃圾回收来释放不再需要的对象所占用的内存。为了确保快速对象分配、短暂的垃圾回收暂停和无内存碎片化,V8采用了一种称为“停止-世界、分代、准确”的垃圾收集器。这意味着V8具有以下特点:
- 在执行垃圾回收循环时,会停止程序的执行。
- 在大多数垃圾回收循环中,只处理对象堆的一部分,以最小化应用程序停止的影响。
- 始终准确地知道所有对象和指针在内存中的位置,避免错误地将对象识别为指针,从而导致内存泄漏。
在V8中,对象堆被分为两个部分:新空间用于创建对象,旧空间用于存放在垃圾回收循环中幸存下来的对象。如果在垃圾回收循环中移动了一个对象,V8会更新所有指向该对象的指针。
分代垃圾收集器通常会在堆之间移动对象,将所有活动对象移动到目标堆中。未移动到目标堆的任何对象都被视为垃圾。目前尚不清楚V8垃圾收集器如何识别活动对象,但我们可以参考其他垃圾收集器的实现来获取一些线索。
以Java的并发标记-清除收集器为例,它是一个有着良好文档记录的垃圾收集器的行为示例。
- 停止应用程序。
- 构建从应用程序代码可达的对象列表。
- 恢复应用程序。与此同时,CMS收集器运行“标记”阶段,将可达对象标记为“非垃圾”。由于这与程序执行并行进行,它还跟踪应用程序所做的引用更改。
- 停止应用程序。
- 运行第二个(“重新标记”)阶段来标记新可达对象。
- 恢复应用程序。与此同时,它“清除”所有被标识为垃圾的对象并回收堆块。
基本上是一个图遍历,从一组特定节点开始。由于无法访问断开连接的对象,它们与其他断开连接的对象之间的连接性不会起作用。
关于Java垃圾回收工作原理有一份很好但有些过时的白皮书,可以在http://www.oracle.com/technetwork/java/javase/memorymanagement-whitepaper-150215.pdf找到。垃圾回收并不仅限于Java,因此我认为Java虚拟机和其他运行时(如JavaScript引擎)采用的各种方法可能存在一些相似之处。
陈纳德写了一篇博客文章,指出释放即将释放的内存对性能会产生负面影响。这个上下文是关于在应用程序关闭时手动释放内存。由于块可能已经被交换到磁盘上,遍历引用的行为可能导致这些块被交换回来。在这种情况下,程序正在遍历那些刚刚被标记为可用并且未被触碰的块。
因此,在操作系统可能已经交换出一些块的情况下,“辅助”垃圾收集器,特别是对于存活时间较长的对象,可能会减慢速度。
而如果你生成的数据不足以引起交换的关注,那么一个合理的垃圾收集器不会花费足够长的时间来注意到这一点。
因此,努力可能是不值得的,而且可能适得其反。尽管删除对堆“dominators”(支配者)的引用可能是有道理的。这些对象,如果被收集,将允许收集许多其他对象。因此,删除对集合本身的引用,但不删除对集合中每个项目的引用。