为什么javascript引擎不使用引用计数和标记和清除垃圾收集?

时间:2013-07-10 15:40:39

标签: javascript garbage-collection

引用计数似乎比标记和清除垃圾收集器更快地删除东西,因为事物可以被释放并且一旦不再使用就会回收内存。标记和扫描旨在解决的问题是捕获循环引用,但作为交换,您必须遍历整个对象树,而其他一切必须在发生这种情况时暂停。

保持引用计数,并且只在内存不足时定期使用标记和扫描是不是更好?标记和扫描GC暂停是一个巨大的痛苦,很难预测或避免。如果引擎支持引用计数,它可能会减少对它们的需求 - 如果你小心避免循环引用,甚至可以为零。

我注意到Python uses this kind of scheme,但可能更多是出于历史原因,而不是故意的表现决定。

1 个答案:

答案 0 :(得分:3)

这是一个艰难的决定,最好的选择并不总是很明确。引用计数确实有优点,但是当你争论时,它并没有“显然”更好:

  • 真正的跟​​踪收集器是分代,增量和并发的某种组合。对于大多数时候,分代GC将“扫描整个堆”转变为“扫描堆的一小块区域”,增量和并发GC消除了大的停顿,有利于理想情况下,许多停顿如此之短,以至于几乎无法对其进行测量。 / LI>
  • 当它变成垃圾时,释放内存并不总是最快的选择。分配通常更快(只是递增指针),如果一次释放大量内存,则可以减少释放(每个字节)的成本。
  • 引用计数需要对每个指针重新分配进行相当多的额外工作,即使与某些跟踪收集器使用的写入障碍相比也是如此。在像JavaScript这样的语言中,这意味着引用计数可能会触发对象五十次,而跟踪只会命中一次。更智能的方案通过引入跟踪收集器的各个方面来避免一些引用计数操作。同样,上述跟踪收集器的优化引入了引用计数的各个方面。有关此二元性的详细信息,请参阅A Unified Theory of Garbage Collection by Bacon et al,以及此问题可能感兴趣的成本模型。
  • Refcounts受到多线程的影响,比跟踪更多,因为更改refcount必须是原子操作并且原子操作相对昂贵(至少一些核心间同步)。无论如何,跟踪要么暂停mutators,要么并发并从线程安全中获得其他好处。我不太了解浏览器引擎内部,知道这是否是一个问题,即使JavaScript名义上是单线程的。