为什么 C 在 malloc 中没有内置的保守垃圾回收

why C doesn't have built-in conservative garbage collection in malloc

我们知道内存泄漏是编写 C 程序时最common/serious 的问题之一。我知道通过调用 free() 来释放内存是程序员的责任,但是将保守的垃圾收集器集成到 malloc 包中仍然是可行的。

当然这个垃圾收集器是保守的,因为 C 不会用类型信息标记内存位置(不像 Java 的成熟垃圾收集器),有C 中的收集器无法推断数据实际上是一个 int,例如,而不是指针。因此,分配器必须保守地将块标记为可达,而实际上它可能不可达。但没关系,它仍然可以正确运行,只是不能释放所有无法访问的块,总比没有好。

那么为什么 C 在 malloc 中没有内置的保守垃圾收集来减轻程序员的负担?

将当前的 malloc 替换为 跟踪收集的垃圾 malloc 会破坏大量 C 代码 。 事实上,保守并不足以让 C 代码正常工作。一个原因是许多 C 代码使用指针 arithmetic/hacking 来提高性能(例如内存 space,执行速度更快)。一个常见的例子是数据结构包含一个基本 32 位指针和几个可用于生成 64 位指针的相关 32 位指针。另一个例子是指针 XORing,尽管很少有代码使用它。

it is better than nothing

在什么方面更好?也许在软件工程方面,但使用 跟踪 GC 可能会显着降低性能 。在大规模并行架构上处理具有许多指针(例如 graphs/trees)的非常大的数据结构的代码尤其如此。应该记住,C 也广泛用于内存 space 通常很紧张的嵌入式系统(没有提到此类平台上 GC 的延迟问题)。

请注意,有几个现有的库在 C 中提供保守的 GC 实现(最著名的可能是 Boehm GC)。

C 是一种旨在尽可能高效地执行的语言。它用于许多 performance-sensitive 上下文,例如内核、嵌入式和 real-time 系统。

因此,尽管在 C 中添加一个 dummy 垃圾收集器是可能的(并且技术上非常简单),但这会降低 C 的性能,因为它需要存储额外的数据管理此 GC(例如,参见 Java 的 GC)。请注意,由于 Jérôme Richard 指出的指针算法,不可能制作完全通用的 C GC。内存块永远不会被指向并且仍然有效(由于间接访问,XOR-ing, ...)

此外,内存管理在 C 中并不 复杂,有经验的 C 程序员很少犯这样的错误(双重释放、内存丢失、读取未初始化的内存,...)。还必须注意的是,有许多调试 C 程序的工具,例如 valgrind,可以轻松检查内存访问的正确性。

总而言之,如果您需要 GC 等高级功能,C 语言不适合您。 C 允许非常高性能的计算,但它的学习曲线非常陡峭,并且更注重性能而不是用户舒适度

许多人偏爱 C,因为它没有有或不需要垃圾回收。有些编译语言与 C 有一些相似之处,比如 Go,确实有它。这些语言很可能会越来越受欢迎。当然,GC 是 Java 世界中的 well-established,并且 Java GC 实现现在非常强大。

但它们不如编写一开始就不需要 GC 的代码那么强大。在某种程度上,GC 越来越受欢迎,因为我们中的许多人工作的环境中,如果我们的程序需要数百兆字节的内存并不重要。毕竟现在内存很便宜。

然而,随着微服务的兴起,人们对 memory-efficient 实现重新产生了兴趣。很难将术语“微”应用于依赖于 GC 的任何事物。

作为开发人员管理自己的内存需要一定的技巧和时间。不仅如此,由于涉及内存管理细节的应用程序代码量很大,像 C 这样的语言通常缺乏表达能力。在某种程度上,这就是您为提高效率而必须付出的代价。周围有很多很好的内存分析工具,它们极大地简化了在 C 代码中查找内存泄漏的过程。如果你是系统的,并且知道你在做什么,那么就不应该有任何内存泄漏。但我们都只是人,很高兴知道有如此强大的工具可以解决这个问题。

总之,有条不紊,懂C的人,应该不会怕没有GC的。