运行时 GC 相对于编译时 ARC 的优势是什么?
What is the advantage of runtime GC over compile-time ARC?
一些较新的语言正在将 ARC 实现到它们的编译器中(Swift 和 Rust,仅举几例)。据我了解,这实现了与运行时 GC 相同的效果(将手动释放程序员的负担从程序员身上移开),同时效率显着提高。
我知道 ARC 可能会成为一个复杂的过程,但随着现代垃圾收集器的复杂性,实现 ARC 似乎不会更复杂。然而,仍然有大量语言和框架使用GC进行内存管理,甚至以系统编程为目标的Go语言也使用GC。
我真的不明白为什么 GC 比 ARC 更可取。我在这里遗漏了什么吗?
这里涉及到很多权衡,这是一个复杂的话题。不过,这是大的:
GC 专家:
- 跟踪垃圾收集器可以处理对象图中的循环。自动引用计数将泄漏内存,除非通过删除引用或找出图形的哪条边应该是弱的来手动打破循环。这是引用计数应用程序实践中相当普遍的问题。
- 跟踪垃圾收集器实际上可以比引用计数快一点(就吞吐量而言),通过并发工作,通过批处理工作,通过延迟工作,以及通过不弄乱缓存接触热循环中的引用计数。
- 复制收集器可以压缩堆,回收碎片页面以减少占用空间
ARC 专家:
- 因为当引用计数达到 0 时对象会立即销毁,所以对象生命周期可用于管理非内存资源。对于垃圾回收,生命周期是不确定的,所以这是不安全的。
- 收集工作通常更加分散,导致暂停时间更短(如果您释放大量对象子图,仍然有可能暂停)
- 因为内存是同步收集的,所以不可能 "outrun the collector" 分配速度快于清理速度。当 VM 分页发挥作用时,这一点尤为重要,因为在退化的情况下,GC 线程命中已被分页的页面,并且远远落后。
- 在相关说明中,跟踪垃圾收集器必须遍历整个对象图,这会强制进行不必要的页面调入(有类似 https://people.cs.umass.edu/~emery/pubs/f034-hertz.pdf 的缓解措施,但它们并未广泛部署)
- 如果跟踪垃圾收集器想要达到其全部吞吐量,通常 "scratch space" 需要比引用计数更多的
我个人对此的看法是,在大多数情况下,真正重要的只有两点是:
- ARC 不收集周期
- GC 没有确定性生命周期
我觉得这两个问题都会破坏交易,但在没有更好的主意的情况下,您只需要选择哪个可怕的问题对您来说更糟糕。
一些较新的语言正在将 ARC 实现到它们的编译器中(Swift 和 Rust,仅举几例)。据我了解,这实现了与运行时 GC 相同的效果(将手动释放程序员的负担从程序员身上移开),同时效率显着提高。
我知道 ARC 可能会成为一个复杂的过程,但随着现代垃圾收集器的复杂性,实现 ARC 似乎不会更复杂。然而,仍然有大量语言和框架使用GC进行内存管理,甚至以系统编程为目标的Go语言也使用GC。
我真的不明白为什么 GC 比 ARC 更可取。我在这里遗漏了什么吗?
这里涉及到很多权衡,这是一个复杂的话题。不过,这是大的:
GC 专家:
- 跟踪垃圾收集器可以处理对象图中的循环。自动引用计数将泄漏内存,除非通过删除引用或找出图形的哪条边应该是弱的来手动打破循环。这是引用计数应用程序实践中相当普遍的问题。
- 跟踪垃圾收集器实际上可以比引用计数快一点(就吞吐量而言),通过并发工作,通过批处理工作,通过延迟工作,以及通过不弄乱缓存接触热循环中的引用计数。
- 复制收集器可以压缩堆,回收碎片页面以减少占用空间
ARC 专家:
- 因为当引用计数达到 0 时对象会立即销毁,所以对象生命周期可用于管理非内存资源。对于垃圾回收,生命周期是不确定的,所以这是不安全的。
- 收集工作通常更加分散,导致暂停时间更短(如果您释放大量对象子图,仍然有可能暂停)
- 因为内存是同步收集的,所以不可能 "outrun the collector" 分配速度快于清理速度。当 VM 分页发挥作用时,这一点尤为重要,因为在退化的情况下,GC 线程命中已被分页的页面,并且远远落后。
- 在相关说明中,跟踪垃圾收集器必须遍历整个对象图,这会强制进行不必要的页面调入(有类似 https://people.cs.umass.edu/~emery/pubs/f034-hertz.pdf 的缓解措施,但它们并未广泛部署)
- 如果跟踪垃圾收集器想要达到其全部吞吐量,通常 "scratch space" 需要比引用计数更多的
我个人对此的看法是,在大多数情况下,真正重要的只有两点是:
- ARC 不收集周期
- GC 没有确定性生命周期
我觉得这两个问题都会破坏交易,但在没有更好的主意的情况下,您只需要选择哪个可怕的问题对您来说更糟糕。