垃圾回收与非垃圾回收编程语言

2022-09-03 03:47:57

因此,如果我理解得很好,垃圾回收会自动释放程序不再使用的对象。就像java中的垃圾收集器一样。

我听说在像C这样的语言中,不支持垃圾回收,程序可能会有内存泄漏,然后耗尽内存。

那么程序员在像C这样的不支持垃圾回收的语言中犯了什么错误呢?我猜想在不再使用对象后不会解除分配对象。但是,由于缺乏垃圾回收器,这些是我们唯一可以犯的错误吗?


答案 1
  • 为您需要的东西分配

  • 不取消分配不再需要的东西(因为你没有很好地跟踪分配/使用/释放)

  • 重新分配已经存在的事物的新实例(未正确跟踪的副作用)

  • 取消分配已释放的内容

  • 取消分配不存在的内容(空指针)

可能还有更多。关键是:管理内存很棘手,最好使用某种跟踪机制和分配/释放抽象来处理。因此,您不妨将其内置到您的语言中,这样它就可以使它对您来说变得轻松有趣。手动内存管理并不是世界末日——它当然是可行的——但是现在,除非你正在编写实时代码、硬件驱动程序或(也许,可能)最新游戏的超优化核心代码,否则手动努力是不值得的,除非作为学术练习。


答案 2

IMO,垃圾回收语言与非垃圾回收语言中存在互补问题。对于每个问题,都有一个非 GC 特征错误和一个 GC 特征错误 - 非 GC 程序员责任和 GC 程序员责任。

GC程序员可能认为他们被免除了释放对象的责任,但是对象拥有内存以外的资源 - 通常需要及时释放的资源,以便可以在其他地方获取它们 - 例如文件句柄,记录锁,互斥锁......

如果非GC程序员有一个悬空的引用(通常不是错误,因为某些标志或其他状态会将其标记为不使用),则GC程序员存在内存泄漏。因此,如果非 GC 程序员负责确保适当地调用 free/delete,则 GC 程序员负责确保将不需要的引用清空或以其他方式适当地处置。

这里有一种说法,即智能指针不处理垃圾循环。这不一定是真的 - 有参考计数方案可以打破周期,也可以确保及时处理垃圾内存,并且至少有一个Java实现使用(并且可能仍然这样做)引用计数方案,该方案可以像C++中的智能指针方案一样容易实现。

引用计数系统中的并发周期收集

当然,这通常不会这样做 - 部分原因是您可能只使用GC语言,但也部分使用IMO,因为它会破坏C++中的关键约定。您会看到,许多C++代码(包括标准库)严重依赖于资源分配是初始化 (RAII) 约定,这依赖于可靠且及时的析构函数调用。在任何应对周期的GC中,您根本无法做到这一点。当中断垃圾循环时,您无法知道首先调用哪个析构函数而没有任何依赖关系问题 - 这甚至可能是不可能的,因为可能有更多的循环依赖关系而不仅仅是内存引用。解决方案 - 在Java等中,不能保证将调用终结器。垃圾回收只收集一种非常特殊的垃圾 - 内存。所有其他资源必须手动清理,就像它们在Pascal或C中一样,并且没有可靠的C++式析构函数的优势。

最终结果 - 大量的清理在C++中变得“自动化”,必须在Java,C#等中手动完成。当然,“自动化”需要引号,因为程序员负责确保为任何堆分配的对象适当地调用delete - 但是在GC语言中,程序员的责任是不同但互补的。无论哪种方式,如果程序员未能正确处理这些职责,你会得到错误。

[编辑 - 在某些情况下,Java,C#等显然可以进行可靠的(如果不一定是及时的)清理,文件就是一个例子。这些是引用循环不会发生的对象 - 要么是因为(1)它们根本不包含引用,(2)有一些静态证明它包含的引用不能直接或间接地引回相同类型的另一个对象,或者(3)运行时逻辑确保链/树/任何可能的循环不是。情况(1)和(2)对于资源管理对象来说是非常常见的,而不是数据结构节点 - 也许是通用的。但是,编译器本身无法合理地保证(3)。因此,虽然编写最重要的资源类的标准库开发人员可以确保对这些资源类进行可靠的清理,但一般规则仍然是不能保证GC能够可靠地清理非内存资源,这可能会影响应用程序定义的资源。

坦率地说,从非GC切换到GC(反之亦然)不是魔杖。它可能会让通常的可疑问题消失,但这只是意味着你需要新的技能来防止(和调试)一组全新的可疑问题。

一个好的程序员应该克服谁是你一边的BS,并学会处理两者。