为什么需要垃圾回收?

4
假设堆上的一个对象超出了范围,为什么程序不能在作用域结束后立即释放内存?如果我们有一个指向对象的指针,该指针被替换为新对象的地址,为什么程序不能在分配新对象之前释放旧对象?我猜这样做不释放内存可以更快速,并且在稍后异步地完成释放,但我不确定。

2
你在这里只考虑了最简单的层次分配-使用-释放场景,但是大多数动态对象都作为复杂数据结构的一部分而存在,例如树、哈希表或各种类型的图形,其中每个对象的生命周期与程序中事件的顺序不同步。 - 500 - Internal Server Error
2个回答

4

垃圾回收为什么是必要的?

严格来说并不是必须的。如果有足够的时间和精力,您总是可以将依赖于垃圾回收的程序转换为不需要垃圾回收的程序。


总的来说,垃圾回收涉及到一种权衡。一方面,垃圾回收允许您编写应用程序而不必担心内存分配和释放的细节(以及由于错误的释放逻辑导致的调试崩溃和内存泄漏的痛苦)。另一方面,垃圾回收的缺点是需要更多的内存。如果没有足够的空闲空间,典型的垃圾收集器效率不高。相比之下,如果您进行手动内存管理,可以编写应用程序以在不再使用堆对象时立即释放它们。此外,在GC执行其操作时,您不会遇到尴尬的“暂停”现象。手动内存管理的缺点是,您必须编写决定何时调用free的代码,并且必须正确。此外,如果您尝试通过引用计数来管理内存,则必须增加和减少每当指针被分配或变量超出范围时的引用计数的成本,必须处理数据结构中的循环,如果您的应用程序是多线程的并且必须处理内存缓存、同步等,则情况会更糟。

就价值而言,如果您使用一个良好的垃圾收集器并适当地进行调整(例如,给它足够的内存等),那么在应用于大型应用程序时GC和手动存储管理的CPU成本是可比较的。

参考资料:


1 - 这是因为现代垃圾收集器的主要成本在于遍历和处理非垃圾对象。如果堆空间不足,导致垃圾很少,那么垃圾回收会做很多工作但回报很少。请参见https://dev59.com/8HE95IYBdhLWcg3wMrAB#2414621进行分析。


为什么编译器不能进行手动内存管理?这样可以避免垃圾回收的缺点,而且由于理论上编译器不应该出错,因此不存在内存管理错误的风险。 - Matthew Inbox
因为大多数编程语言的编译器不能做到这一点。对于除了动态存储的最简单用法之外,它涉及运行时计算。(还有之前提到的引用循环问题。)但是看看 Rust 处理这个问题的方式:https://doc.rust-lang.org/1.22.0/book/first-edition/the-stack-and-the-heap.html,http://willcrichton.net/notes/rust-memory-safety/ - Stephen C
非常有趣的链接。我很惊讶,因为一些人(可能是千禧一代的程序员)认为GC语言是设计错误。除了这篇论文之外,还有其他历史方面的见解吗? - mavavilj

0

这更加复杂,但是

1)如果在作用域结束之前存在内存压力怎么办?作用域只是一种语言概念,与可达性无关。因此,一个对象可以在它超出作用域之前被“释放”(Java GC定期执行此操作)。此外,如果您在每个作用域完成后释放对象,则可能会做太少的工作。

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

总的来说,您所描述的建议并没有什么问题,事实上,从高层次的角度来看,这几乎正是Rust编程语言的工作方式。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接