【问题标题】:Why is garbage collection necessary?为什么需要垃圾收集?
【发布时间】:2020-05-25 09:27:26
【问题描述】:

假设堆上的一个对象超出范围。为什么程序不能在作用域结束后立即释放内存?或者,如果我们有一个指向一个对象的指针被一个新对象的地址替换,为什么程序不能在分配新对象之前释放旧对象?我猜不立即释放它会更快,而是在稍后的时间点异步完成释放,但我不太确定。

【问题讨论】:

  • 您在这里只考虑最简单、分层的无分配使用场景,但大多数动态对象都是作为复杂数据结构的一部分生活的,如树、哈希表或各种图表,其中每个对象的生命周期与程序中事件的顺序不同步。

标签: garbage-collection


【解决方案1】:

为什么需要垃圾收集?

这不是绝对必要的。只要有足够的时间和精力,您总是可以将依赖垃圾收集的程序转换为不依赖垃圾收集的程序。


一般来说,垃圾回收需要权衡取舍。

一方面,垃圾收集允许您编写应用程序而不用担心内存分配和释放的细节。 (以及由于释放逻辑错误而导致调试崩溃和内存泄漏的痛苦。)

垃圾回收的缺点是需要更多内存。如果没有足够的空闲空间1,典型的垃圾收集器效率不高。

相比之下,如果您进行手动内存管理,您可以编写应用程序以在不再使用堆对象时立即释放它们。此外,当 GC 执行它的操作时,您不会感到尴尬的“暂停”。

手动内存管理的缺点是您必须编写决定何时调用free的代码,并且您必须正确处理。此外,如果您尝试通过引用计数来管理内存:

  • 每当分配指针或变量超出范围时,都会增加和减少引用计数,
  • 您必须处理数据结构中的循环,并且
  • 当您的应用程序是多线程的并且您必须处理内存缓存、同步等时,情况会更糟。

对于它的价值,如果你使用一个像样的垃圾收集器并适当地调整它(例如,给它足够的内存等),那么当你将它们应用于大型应用程序时,GC 和手动存储管理的 CPU 成本是相当的。

参考:


1 - 这是因为现代收集器的主要成本是遍历和处理非垃圾对象。如果因为你对堆空间的吝啬而没有很多垃圾,那么 GC 做了很多工作却很少有回报。请参阅https://stackoverflow.com/a/2414621/139985 进行分析。

【讨论】:

  • 为什么编译器不能手动管理内存?您避免了垃圾收集的缺点,并且没有出现内存管理错误的风险,因为编译器理论上不应该犯任何错误。
  • 因为编译器无法为大多数编程语言做到这一点。它涉及运行时计算,除了最简单的动态存储用途。 (还有前面提到的引用循环问题。)但是看看 Rust 处理这个问题的方式:doc.rust-lang.org/1.22.0/book/first-edition/…, willcrichton.net/notes/rust-memory-safety
  • @StephenC 你的脚注是什么意思?
  • @Snork 添加了链接
【解决方案2】:

比较复杂,但是

1) 如果在范围结束之前存在内存压力怎么办?范围只是一个语言概念,与可达性无关。所以一个对象可以在它超出范围之前“释放”(java GCs 会定期这样做)。另外,如果你在每个作用域完成后释放对象,你可能会太频繁地做太少的工作

2) 就引用而言,您没有考虑到引用可能具有层次结构,并且当您更改一个层次结构时,必须有遍历这些层次结构的代码。发生这种情况时,可能不是正确的时间。

总的来说,你描述的这样一个提议没有任何问题,事实上,从高层次的角度来看,这几乎正是 Rust 编程语言的工作原理。 p>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-10
    • 1970-01-01
    • 2012-04-14
    • 1970-01-01
    相关资源
    最近更新 更多