有没有特定的原因垃圾收集不是为C设计的?

Rav*_*mer 28 c garbage-collection

我听说C自动收集垃圾是不是最理想的 - 这有什么道理吗?

是否有特定原因没有为C实现垃圾收集?

R..*_*R.. 55

不要听"C老了,这就是为什么它没有GC"的人.GC存在一些基本问题无法克服,这使得它与C不兼容.

最大的问题是准确的垃圾收集需要能够扫描内存并识别遇到的任何指针.某些更高级语言限制整数不使用所有可用位,因此可以使用高位来区分对象引用和整数.然后,这些语言可以在特殊的字符串区域中存储字符串(可以包含任意八位字节序列),在这些区域中它们不能与指针混淆,并且一切都很好.但是,AC实现不能这样做,因为字节,更大的整数,指针和其他所有内容可以一起存储在结构,联合或作为返回的块的一部分malloc.

如果您丢弃准确度要求并决定一些对象永远不会被释放,那会怎么样?因为程序中的某些非指针数据与这些对象的地址具有相同的位模式?现在假设您的程序从外部世界(网络/文件/等)接收数据.我声称我可以让你的程序泄漏任意数量的内存,并最终耗尽内存,只要我能够猜出足够的指针并在我为程序提供的字符串中模拟它们.如果您应用De Bruijn序列,这会变得更容易.

除此之外,垃圾收集速度很慢.你可以找到数百名喜欢以其他方式声称的学者,但这不会改变现实.GC的性能问题可分为3大类:

  • 不可预测性
  • 缓存污染
  • 花在所有记忆上的时间

现在声称GC很快的人只是将它与错误的东西进行比较:写得不好的C和C++程序每秒分配和释放数千或数百万个对象.是的,这些也会很慢,但至少可以预测,如果有必要可以测量和修复.一个编写良好的C程序将花费很少的时间malloc/ free开销甚至无法衡量.

  • 引用计数和垃圾收集不是一回事.引用计数干净而优雅,但不能自动释放循环链接列表和其他循环引用的对象,并且很难在多线程环境中使其高效,其中不同的线程可能试图同时递增或递减引用计数. (20认同)
  • @JeremyP:他们受到我在帖子中解释的缺陷的影响.有人可以写一个有缺陷的,易受攻击的实现这一事实与我声称无法编写正确,强大的实现的说法并不矛盾. (17认同)
  • @JeremyP:这是不可能的.这是我答案的精髓.在攻击者的控制下泄漏内存的程序不是远程可靠的,更不是"足够强大". (8认同)
  • -1我可以想到至少有两个垃圾收集器用于C顶部.所以"GC存在的基本问题是无法克服的,这使得它与C不兼容"是一种错误的陈述. (6认同)
  • 一个很好的答案R ..! (2认同)
  • “你可以找到成百上千的学者喜欢另辟蹊径,但这不会改变现实”,“如今声称 GC 很快的人只是将其与错误的东西进行比较:写得不好的 C”——可以你提供一些参考?我读过很多关于 GC 性能的论文,但没有一篇像这样读过。如果我们因为不认为研究反映“现实”而打折研究,我们应该打折*所有* CS研究吗? (2认同)

Jer*_*fin 26

垃圾收集被实施了C(例如,勃姆-德默斯-韦瑟集电极).出于多种原因,C未被指定为包含GC,主要是因为他们所针对的硬件和他们正在构建的系统,它只是没有多大意义.

编辑(回答一些其他提出的指控):

  1. 为了使保守的GC定义良好,你基本上只需对语言进行一次更改:说任何使指针暂时"不可见"的行为都会导致未定义的行为.例如,在当前的C中,您可以将指针写入文件,覆盖内存中的指针,稍后将其读回,并且(假设它之前有效)仍然可以访问它指向的数据.GC不一定"意识到"指针存在,因此它可以看到内存不再可访问,因此可以打开收集,因此后来的解引用不会"起作用".

  2. 至于垃圾收集是非确定性的:有实时收集器是绝对确定的,可用于硬实时系统.还有确定性的堆管理器用于手动管理,但大多数手动管理器不是确定性的.

  3. 至于垃圾收集缓慢和/或破坏缓存:从技术上讲,这是真的,但它纯粹是一种技术性.虽然(至少大多数情况下)避免这些问题的设计(例如,代际清除)是众所周知的,但可以认为它们并不完全是垃圾收集(尽管它们对程序员来说几乎完全相同).

  4. 对于在未知或意外时间运行的GC:与手动管理的内存相比,这不一定或多或少.您可以在一个单独的线程中运行GC,该线程运行(至少在某种程度上)是不可预测的.通过手动内存管理来合并空闲块也是如此.分配内存的特定尝试可能会触发收集周期,导致某些分配比其他分配慢得多; 使用惰性块的惰性合并的手动管理器也是如此.

  5. 奇怪的是,GC 与C++的兼容性远远低于 C++.大多数C++依赖于确定性地调用析构函数,但垃圾收集不再是这种情况.这打破了许多代码 - 编写代码越好,它通常会导致更大的问题.

  6. 同样,C++要求std::less<T>为指针提供有意义的(更重要的是,一致的)结果,即使它们指向完全独立的对象.使用复制收集器/清道夫需要一些额外的工作来满足这个要求(但我很确定它是可能的).处理(例如)某人散列地址并期望获得一致结果更加困难.这通常是一个糟糕的想法,但它仍然可能,并应产生一致的结果.

  • @R:至少IMO,你的回答并没有指出真正无法弥补的缺陷.相反,它指的是一个主要是理论上的问题,即使在相当罕见的情况下它可能变得很重要,它也很容易解决.这并不是说GC无论如何都是完美的,但是你的回答并没有让我特别准确地表现出它的优点或缺点. (2认同)

win*_*aed 18

C是在20世纪70年代早期发明的,用于编写操作系统和其他低级内容.垃圾收集器在附近(例如,Smalltalk的早期版本),但我怀疑他们是否能够在如此轻量级的环境中运行,并且使用非常低级别的缓冲区和指针会有所有复杂性.

  • +1用于指出在创建C时GC是*可用*.许多人似乎忘记了所有这些"现代的花里胡哨"已经存在(以某种形式或者另一种形式)已有数十年了. (4认同)

Cro*_*yer 16

对不起,但是说C没有GC,因为它已经老了,或者因为它是针对不同的硬件而设计的,这有点荒谬.

问为什么C没有GC,但是其他语言有点像问"为什么锤子有一个钝端,而斧头有一个尖锐的结束"?嗯,语言是工具,不同的工具意味着构建具有不同要求的不同程序.某些程序(如设备驱动程序或操作系统内核)的要求几乎不可能实现垃圾收集,同时仍然满足它们,因此需要像C这样的语言,因此创建了C语言.

就像其他工具一样,它就是这样,因为它要解决的问题迫使它成为那样.说这是因为它是在70年代创建的,或者是因为旧的硬件使得它看起来像完全是间接的.如果这是真的,C会在70年代消失,但它仍然是一种流行的语言.

此外,如果没有某种运行时系统,gc很难干净地实现,但C意味着可以在裸硬件上运行.

  • 我比其他答案更喜欢最后一段. (6认同)

wll*_*cnt 11

C是一种非常古老的语言,缺乏许多现代语言的钟声和哨声.要添加垃圾收集,现在需要重新调整语言.一般来说,任何愿意对C进行许多更改的人都会创建自己的语言.

将自动垃圾收集添加到语言上通常会降低性能,或者会导致垃圾收集在非常时间发生.将垃圾收集添加到C会导致它失去其中一个比较优势,因为它可以用于需要实时或接近实时响应时间的系统级编程.

  • 我会在这里说清楚,虽然我同意你的答案一般,但没有实时的事情.所有"实时"程序都按照所需的速度运行 - 您可以拥有在几小时或几天内响应的"实时"系统.我认为你所寻找的概念是"确定性的".添加GC会使C的确定性降低,因此更难以验证它是否会在当天用户期望所需的时间内达到所需的响应. (16认同)
  • 不幸的是,大部分内容都是完全无稽之谈.允许保守的GC只需要少量*(通常是微不足道的)更改.允许"精确"GC需要更多更改,但仍然*很好*缺少"主要参与".实时垃圾收集器提供了比大多数malloc/calloc/realloc/free实现中使用的堆管理器更多*确定性的时序. (8认同)
  • @R:请原谅我的直率,但你似乎并不知道你在谈论什么.虽然肯定不是全新的,但ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps给出了真正的分配器如何工作的一些想法. (4认同)
  • 通常,自动内存管理实际上比普通程序员手中的手动内存管理更好.理论上*可以手工制作完美的内存管理,但实际上并非如此.永远.事实上,实际上(在确定性意义上)有能力的垃圾收集器可用.例如,贝克在1978年概述了一个,并在1992年对其进行了改进.注意,*改进*版本已经过去了十多年.基本上,忽略你认为你对GC的所有了解.你可能有点尴尬. (2认同)

Bob*_*phy 7

作为一种语言,C被故意设计为非常小.它具有很少的内在操作或功能,其中大多数反映了CPU中的基本指令.它通常被称为"可移植汇编语言".

这使得编写可移植程序以切换电话呼叫变得非常好,这些程序在内存很少的小型计算机上运行,​​这是贝尔实验室在他们第一次使用C和Unix时70年代早期的一部分.

这也使C非常适合编写OS内核,设备驱动程序等.这样的可执行文件不能指望拥有丰富,复杂的运行时环境,如垃圾收集 - 甚至只是动态内存分配,如malloc().那是因为它们实际上构成了这些环境的基础,所以你会遇到"通过你的引导拉动自己"的问题.

人们已经为C编写了垃圾收集的内存分配库.但是库并不是C语言本身的一部分,而且复杂的东西也不太可能被接受为标准C库的一部分.C程序员对标准的更改非常保守.


bel*_*daz 5

C是一种非常低级的语言.这是一种语言,您可以选择使用诸如垃圾收集之类的东西来编写更高级别的语言.它既小又简单,完全符合您的要求.

C++需要在C上构建并添加更复杂/自动的内存管理(比如当对象超出范围时调用析构函数).然后你可能想知道为什么C++没有垃圾收集,在这种情况下看看Stroustrup 什么说法:简单地,人们想要以更直接的方式做事,而真正想要它的人可以使用库(也可以用于C).


Pro*_*ica 5

我喜欢JWZ关于C.的说法:-)

C是"认为它是语言的PDP-11汇编程序".

所以,如果你看一下你将如何建立一个便携式汇编,那么C看起来没有垃圾回收完全正确的,原因很简单,CPU没有一个" 垃圾收集 "指令.*

(虽然大多数其他CPU指令被捕获就好了与C这一天,有些则不是,虽然,像CS'缺乏表现力的SIMD操作等)

*是的,我知道有些人会找到一个证明我错的例子.但在一般情况下......