为什么不是C ++有一个垃圾收集器?(Why doesn't C++ have a garb

2019-07-19 07:16发布

我没有问,因为垃圾收集首先的优劣这个问题。 我提出这项主要原因是,我知道了Bjarne Stroustrup的曾表示,C ++会在某个时间点,垃圾收集器。

随着中说,为什么还没有它已添加? 目前已经对C ++的一些垃圾收集。 这只是其中的一个类型的东西“谈何容易”? 或者是还没有被添加(并且不会被添加在C ++ 11)有其他原因?

交叉链接:

  • 垃圾收集器用于C ++

只是为了澄清,我明白了为什么C ++没有一个垃圾收集器时,首次建立它的原因。 我不知道为什么收集器不能被添加。

Answer 1:

隐式垃圾收集可能已经被添加的,但它只是没有获得晋级。 可能是由于不只是实现的并发症,而且由于人们不能够得出一个普遍的共识不够快。

从Bjarne的Stroustrup的自己一个报价:

我曾希望垃圾收集器可以被选择性地使能是C ++ 0x中的一部分,但有我有,以与这样的集电极与语言的其余部分如何整合只是一个详细的规范做足够的技术问题如果提供的。 由于是与基本上所有的C ++ 0x特征的情况下,实验性实现存在。

有话题商量好了这里 。

总体概述:

C ++是非常强大的,可以让你做几乎任何事情。 出于这个原因,它不会自动推很多东西到你可能会影响性能。 垃圾收集器可以用智能指针的对象(包装用的指针引用计数,其中自动删除自己,当引用计数为0),可以轻松实现。

C ++的设计充分考虑竞争者没有垃圾收集建成。 效率是C ++不得不从比较到C和其他人抵挡批评的主要关注点。

有2种类型的垃圾收集...

显式垃圾收集:

的C ++ 0x将通过与shared_ptr的创建指针有垃圾收集

如果你想,你可以使用它,如果你不想要它,你不会被迫使用它。

目前,您可以使用boost:shared_ptr的,以及如果你不想等待的C ++ 0x。

隐式垃圾收集:

它没有透明的垃圾回收虽然。 这将是未来的C ++规范一个焦点,但。

为什么TR1没有隐含的垃圾收集?

有很多的事情的C ++ 0x的TR1应该有,Bjarne的Stroustrup的在之前的采访中表示,TR1没有,就像他所希望的。



Answer 2:

为了增加在这里的辩论。

有垃圾收集已知问题,并了解它们有助于理解为什么有没有在C ++中。

1.性能?

第一个投诉往往是关于性能,但大多数人并不真正知道他们在说什么。 如所示Martin Beckett的问题可能不是性能本身,而是性能的可预测性。

目前被广泛部署GC的2个系列:

  • 马克 - 扫描样
  • 引用计数样

Mark And Sweep更快(对整体性能的影响较小),但它从一个“冻结世界”遭受综合征:即当在,其他一切都停止在GC踢,直到GC作出了清理。 如果你想建立一个在几毫秒内接听服务器...有些交易将不辜负您的期望:)

问题Reference Counting是不同的:因为你需要有一个原子计数引用计数的开销增加了,尤其是在多线程环境。 此外,还有的参考周期的问题,所以你需要一个聪明的算法(用“冻结世界”也是如此,虽然不太频繁一般实行)来检测这些周期并消除它们。 在一般情况下,截至今天,这种(尽管通常更敏感或更确切地说,冻结较少)比慢的Mark And Sweep

埃菲尔实施者是试图实现我有一个看到文件Reference Counting垃圾收集器,将有一个类似的全球性能Mark And Sweep无“冻结世界”方面。 它需要对GC(典型值)的一个单独的线程。 该算法是有点可怕(末),但纸发言介绍了概念之一的时间和显示从“简单”版本的全面的一个算法的进化的一个好工作。 推荐阅读如果只有我可以把我的手回到了PDF文件...

2.资源获取就是初始化

这是一个常见的成语C++ ,你将包装对象中的资源的所有权,以确保他们得到适当的释放。 它主要是用于内存,因为我们没有垃圾回收,但它也是有用仍然为许多其他情况:

  • 锁(多线程,文件句柄,...)
  • 连接(到数据库,另一台服务器,...)

我们的想法是适当控制对象的生命周期:

  • 因为你需要它,它应该是只要活着
  • 当你用它做它应该被杀死

GC的问题是,如果它有助于前者,最终保证了以后......这个“最终”可能是不够的。 如果你释放锁,你真的喜欢它现在被发布,所以它不会阻止任何进一步的来电!

与GC的语言有两种变通:

  • 不使用GC时,堆栈分配是不够的:这是正常的性能问题,但是对我们来说它确实有助于自范围定义寿命
  • using构造......但它是明确的(弱)RAII而在C ++ RAII是隐式的,使用户无法在不知不觉中做出错误(通过省略using关键字)

3.智能指针

智能指针经常出现作为银子弹在处理存储器C++ 。 很多时候,我听到:我们不需要GC毕竟,因为我们有智能指针。

人们不可能是十分错误的。

智能指针做帮助: auto_ptrunique_ptr使用RAII的概念,是非常有用的确实。 他们是如此简单,你可以通过自己很容易写。

当一个人需要共享所有权但它变得更加困难:你可能会在多个线程之间共享,并有与计数的操控几个微妙的问题。 因此,一个自然走向云shared_ptr

这是伟大的,这就是提振毕竟,但它不是万能的。 事实上,主要的问题shared_ptr是,它通过模拟实现的GC Reference Counting ,但你需要自己都实现了循环检测... URG

当然有这个weak_ptr啄,但我遗憾的是已经看到了内存泄漏,尽管使用的shared_ptr ,因为这些周期的......当你在一个多线程环境,这是非常难以察觉!

4.有什么解决办法?

没有银弹,但一如既往,这绝对是可行的。 在没有GC之一必须所有权明确:

  • 优选具有在一个给定时间一个所有者,如果可能的话
  • 如果没有,请确保您的类图没有任何循环有关的所有权和使用的微妙的应用打破他们weak_ptr

因此,我们确实,这将是巨大的,有一个GC ......不过这是非同小可的问题。 而在此同时,我们只需要挽起袖子。



Answer 3:

什么类型? 它应该为嵌入式洗衣机控制器,蜂窝电话,工作站或超级计算机可以优化?
它应该优先GUI响应或服务器负载?
它应该使用大量CPU的内存或手?

C / C ++是在太多的不同的情况下使用。 我怀疑类似升压智能指针将足以满足大多数用户

编辑 - 自动垃圾收集器都没有这么多的性能问题(你可以随时购买更多的服务器),它是可预测,性能的问题。
当GC会踢不知道是喜欢采用患发作性睡眠航空公司飞行员,大部分时间他们都是伟大的 - 但是当你真正需要的响应!



Answer 4:

其中一个是C ++没有内建垃圾收集的最大原因就是让垃圾收集发挥与析构函数漂亮真的,真的很难。 据我所知,没有人真正知道如何解决这个问题完全呢。 有很多的问题需要解决:

  • 对象的确定性寿命(引用计数为您提供了这一点,但GC没有。虽然这可能不是什么大不了的事这一点)。
  • 如果对象被垃圾回收时,析构函数抛出,会发生什么? 大多数语言忽略这个例外,因为那里有真的没有catch块,以便能够运输到,但是这可能不是对于C可接受的解决方案++。
  • 如何启用/禁用它? 当然这很可能是由编译时决定但这是GC VS是不GC编写的代码将是非常不同的,可能不兼容编写的代码。 你如何调和呢?

这些仅仅是少数的所面临的问题。



Answer 5:

尽管这是一个问题,但还有一个问题,我没有看到有人已经解决所有:垃圾收集几乎是不可能的指定。

特别是,C ++标准是相当慎重指定语言的外部观察行为方面,而不是执行如何实现这种行为。 在垃圾回收的情况下,然而, 几乎没有任何外部观察到的行为。

垃圾收集的总体思路是,它应该确保把内存分配会成功的一个合理的尝试。 不幸的是,它本质上是不可能保证所有内存分配会成功,即使你在操作的垃圾收集器。 这是真实的在任何情况下一定程度上,但特别所以在C ++的情况下,因为它是(可能)无法使用复制收集器(或任何类似),一个收集周期中移动内存中的对象。

如果你不能移动的物体,你不能创建一个单一的,连续的内存空间,从中做你的分配 - 这意味着您的堆(或自由存储,或任何你喜欢称呼它)就可以了,而且很可能会,随着时间的推移碎片。 这反过来,可以防止分配得手,即使有比所请求的量的游离更多的内存。

虽然有可能拿出一些保证说,(在本质上),如果你反复地精确重复分配相同的模式,并且成功地在第一时间,将继续在随后的迭代中获得成功,前提是所分配的内存成为迭代之间无法访问。 这是这样一个弱保证它基本上是无用的,但我不能看到它加强的任何合理的希望。

即便如此,它比已经提出了C ++更强。 在先前的提案 [警告:PDF](即得到下降)并不能保证在所有的东西。 在28页的建议,你在从外部观察到的行为方式得到的是一个(非规范性的)纸条,上面写着:

[注:对于垃圾回收方案,高品质的托管实现应该尝试最大化回收不可到达的内存量。 末端注]

至少对我来说,这引起了人们对投资回报率一个严肃的问题。 我们要打破现有的代码(没人确信究竟有多大,但肯定相当多的),放在实现和代码新的限制新的要求,是我们得到什么样的回报很可能什么都没有?

即使在最好的,我们得到的是,根据程序与Java测试 ,将可能左右六倍之多的内存要求以他们现在做同样的速度运行。 更糟的是,垃圾收集是从一开始的Java的一部分- C ++的地方够垃圾收集更多的限制,这将几乎肯定有一个更糟糕的成本/效益比(即使我们超越提议保证什么,假设会有一些好处)。

我数学总结情况:今年复杂的局面。 正如所有的数学家都知道,复数由两个部分组成:实部和虚。 这在我看来,我们这里是(至少大部分)虚构成本是真实的,但好处。



Answer 6:

如果你想自动垃圾收集,也有C ++良好的商业和公共领域的垃圾收集器。 对于其中垃圾收集是合适的应用程序,C ++是具有与其他垃圾收集的语言相媲美一个性能优良的垃圾收集的语言。 见C ++编程语言(4RD版)自动垃圾收集在C ++的讨论。 又见汉斯-J。 贝姆的网站C和C ++垃圾收集 ( 归档 )。

此外,C ++支持的编程技术,其允许存储器管理是安全的和隐含没有垃圾收集器 。 我认为垃圾收集最后的选择和处理资源管理不完善的方式。 这并不意味着它是永远不会有用的,只是有在许多情况下更好的方法。

来源: http://www.stroustrup.com/bs_faq.html#garbage-collection

至于为什么它没有它内置的,如果我没有记错它被发明之前,GC是事情 ,我不相信语言可以有GC有几个原因(IE向后兼容性与C)

希望这可以帮助。



Answer 7:

斯特劳斯对这个在2013年提出了一些很好的意见融入本土发布会。

只是跳到关于25m50s 视频 。 (我建议你看整个视频实际上,但这跳到关于垃圾回收的东西。)

当你有一个真正伟大的语言,可以很容易(和安全,可预测的,并且易于读取,并且易于教)来处理直接的方式对象和值,避免(明确的)使用的堆,那么你甚至不想垃圾收集。

随着现代C ++,和我们在C ++ 11的东西,垃圾收集不再是可取的,除了在有限的情况下。 事实上,即使一个良好的垃圾收集器内置到主要的C ++编译器中的一个,我认为它不会被经常使用。 它会更容易 ,更轻松,避免了GC。

他表示这个例子:

void f(int n, int x) {
    Gadget *p = new Gadget{n};
    if(x<100) throw SomeException{};
    if(x<200) return;
    delete p;
}

这是用C ++不安全。 但它在Java中也是不安全的! 在C ++中,如果函数返回早期的delete将永远不会被调用。 但是,如果你有充分的垃圾收集,比如在Java中,你只是得到一个建议,即对象将被销毁“在未来的某个时候”( 更新:它甚至更糟,这Java 承诺永远叫终结。 - 它也许永远不会被调用)。 这是不够好,如果小工具拥有一个打开的文件句柄,或者你在以后的缓冲写入到数据库到数据库的连接,或数据。 我们希望小工具,以尽快,它的完成销毁,以尽快释放这些资源。 你不希望你的数据库服务器与成千上万的不再需要的数据库连接的挣扎 - 它不知道你的程序完成工作。

那么,有什么解决办法? 有几个方法。 最明显的方法,您可以使用它绝大多数的对象是:

void f(int n, int x) {
    Gadget p = {n};  // Just leave it on the stack (where it belongs!)
    if(x<100) throw SomeException{};
    if(x<200) return;
}

这需要更少的字符输入。 它没有new的方式获得。 它不要求你输入Gadget的两倍。 该对象在函数的末尾被破坏。 如果这是你想要的东西,这是非常直观的。 Gadget小号行为相同, intdouble 。 可以预测,易于阅读,易于教。 一切都是“价值”。 有时,一个大的价值,但价值是容易教,因为你没有,你的指针(或引用)得到的东西这个“超距作用”。

大多数您做出的对象仅用于创建它们,或许作为输入到子功能通过功能的使用。 程序员不应该返回对象时,或者以其他方式在整个软件的广泛分离的部分共享的对象来思考“内存管理”。

范围和使用寿命是非常重要的。 在大多数情况下,如果寿命是相同的范围更容易。 它更容易理解和容易教。 当你想要一个不同的生命周期,它应该是显而易见的读取你这样做的代码,通过使用shared_ptr的例子。 (或由值返回(大)的对象,利用移动的语义或unique_ptr

这似乎是一个效率问题。 如果我想要什么,从返回一个小工具foo() C ++ 11的移动语义更容易地返回大对象。 只要写Gadget foo() { ... }这将只是工作,并迅速开展工作。 你并不需要乱用&&自己,只是返回值的东西,语言往往能够做到必要的优化。 (即使是C ++ 03之前,编译器在没有避免不必要的复制一个非常好工作。)

由于斯特劳斯视频别处说过(意译):?“只有一位计算机科学家将坚持对复制的对象,然后破坏原有的(众笑)为什么不直接移动对象到新的位置,这是人类(不是计算机科学家)期望“。

当你能保证只需要一个对象的副本,它更容易理解对象的生命周期。 你可以选择你想要的生存期策略和垃圾回收是有,如果你想要的。 但是,当你理解了其他方法的好处,你会发现,垃圾收集是在你的喜好列表的底部。

如果不适合你,你可以使用unique_ptr ,或做不到这一点, shared_ptr 。 写得很好的C ++ 11短,比许多其他语言更易于阅读,更易于教,当涉及到内存管理。



Answer 8:

后面的C ++的想法是,你不会支付你不使用的功能的任何性能影响。 因此,增加垃圾收集就意味着有一些程序上的硬件直线行驶某种运行虚拟机的C之内的方式做一些。

没有什么能阻止你使用某种形式的绑定到一些第三方的垃圾回收机制智能指针。 我似乎记得微软做这样的事情,与COM,并没有去很好。



Answer 9:

为了回答关于C ++最“为什么”的问题,读设计和C演化++



Answer 10:

因为现代C ++不需要垃圾收集。

Bjarne的Stroustrup的FAQ 对此事的回答说 :

我不喜欢垃圾。 我不喜欢乱丢垃圾。 我的理想是消除由不产生任何垃圾垃圾收集器的需要。 这成为可能。


这种情况,对于编写的代码,这些天(C ++ 17和之后的官方核心准则 )如下:

  • 大多数内存所有权相关的代码库(尤其是那些提供容器)。
  • 大多数使用的内存涉及所有权代码如下RAII 模式 ,所以分配的建设和回收销毁,退出其中一些被分配范围时发生的制造。
  • 你并没有明确分配或直接释放内存 。
  • 原始指针没有自己内存 (如果你遵循的准则),所以你不能四处传递它们泄漏。
  • 如果你想知道你将如何通过在内存中值序列的起始地址-你会做的是一个跨度 ; 没有原始指针需要。
  • 如果你真的需要一个拥有“指针”,您可以使用C ++ 标准库的智能指针 -他们不能泄漏,是相当有效的。 另外,您也可以通过跨范围界限所有权“所有者指针” 。 这些都是罕见的,必须明确的使用; 他们允许部分静态检查,对泄漏。

“哦,是吗?可是你知道...

...如果我只是写代码,我们以前写C ++在旧时代的方式吗?”

事实上,你可能只是不顾一切的方针,写漏的应用程序代码-它会编译和运行(和泄漏),与往常一样。

但它不是一个“只是不这样做”的局面,其中,开发商预计将良性和锻炼了很多自我控制的; 它只是没有简单写不合格的代码,也不是快写,也不是效果较好。 渐渐地它也将成为写更多的困难,因为你将面临越来越“阻抗不匹配”什么符合代码提供,并期望。

...如果我reintrepret_cast ? 或做指针运算? 或其他类似的黑客?”

事实上,如果你把你的心给它,你可以写,尽管踢得好看与准则,食堂的东西了代码。 但:

  1. 你会做这种情况很少(在代码中的地方而言,不一定是在执行时间分数计)
  2. 你只做到这一点故意,不可不慎。
  3. 这样做将在符合准则的代码库中脱颖而出。
  4. 这是一种代码中,你会绕过GC另一种语言呢。

...图书馆的发展?”

如果你是一个C ++库开发人员,那么你做的写,涉及原始指针不安全的代码,你需要认真和负责任代码 - 但这些代码的自包含块由专家(更重要的是,通过专家审查)编写的。


所以,它就像Bjarne的说:真的没有动机通常收集垃圾,因为你所有,但确保不产生垃圾。 GC正在成为一个非问题,C ++。

这并不是说GC不是为某些特定应用一个有趣的问题,当你想使用自定义的分配和取消分配策略。 对于那些你想定制的分配和重新分配,而不是语言级GC。



Answer 11:

一个原始的C语言背后的基本原则是,内存是由字节序列,并且代码只需要关心的这些字节的意思是在说他们正在使用的准确时间。 现代C使编译器可施加额外的限制,但C包括 - 和C ++保留 - 分解的指针成一个字节序列,组装含有相同的值成一个指针任何字节序列,并且然后使用该指针的能力访问较早的对象。

虽然这种能力可能是有用的 - 甚至是不可或缺的 - 在某些类型的应用中,包括能力,将在其支持任何类型的有用和可靠的垃圾收集的能力是非常有限的语言。 如果编译器不知道已经与由一个指针位所做的一切,也无法知道是否足以重建指针信息可能在宇宙的某个地方存在的方式。 因为它是可能存储的方式,计算机将无法访问,即使它知道他们(例如,可能已显示的屏幕足够长的人写上弥补指针字节的信息他们在一张纸上),也可能是根本不可能的计算机知道是否指针可能可能在将来被使用。

许多垃圾收集框架的一个有趣怪癖是一个对象的引用不被包含在其中的位模式所定义,而是由在对象引用和其他地方持有的其他信息保持的比特之间的关系。 在C和C ++,如果存储在一个指针的位模式标识对象,该位模式将确定,直到对象被明确地销毁该对象。 在典型的气相色谱系统中,对象可以通过将位图案0x1234ABCD在一个时间瞬间表示,但下一个GC周期可能与到0x4321BABE引用替换到0x1234ABCD所有参考文献,于是对象将被后者图案来表示。 即使一个人以显示与一个对象引用,然后相关联的位图案日后读取从键盘背面,就没有期望相同的位模式将是可用于识别相同的对象(或其它对象)。



Answer 12:

所有的技术说话是过分复杂的概念。

如果你把GC到C ++所有的内存会自动再考虑像Web浏览器。 Web浏览器必须加载一个完整的网页文件并运行网页脚本。 你可以存储在文档树web脚本变量。 在有很多标签打开的浏览器中的大文件,这意味着每次GC必须做一个完整的集合,也必须扫描所有的文档元素。

在大多数计算机上,这意味着页面错误会发生。 所以,最主要的原因,回答的问题是,页面错误会发生。 你会知道这个时候您的PC开始做大量的磁盘访问的。 这是因为GC必须接触大量的内存,以证明无效的指针。 当你使用大量内存善意的应用程序,不必扫描所有对象的每集合是因为​​页面错误的破坏。 页面错误是当虚拟内存需要得到读回从磁盘RAM。

所以,正确的解决方案是一个应用程序划分为需要GC的部件和不部分。 在Web浏览器实例以上的情况下,如果文档树使用malloc分配,但JavaScript和GC运行,则每次在它的GC踢只扫描的记忆一小部分和内存的所有调出元素文档树并不需要得到分页回来。

为了进一步了解这个问题,查找关于虚拟内存和它是如何在计算机上实现。 这是所有的事实,2GB的可用于程序的时候确实没有那么多内存。 在具有2GB RAM的32位系统,现代计算机是不是这样只提供一个程序运行的问题。

作为一个附加的例子,考虑一个完整的集合,它必须跟踪的所有对象。 首先,你必须通过根可达扫描所有对象。 第二扫描所有在步骤1中可见的对象然后扫描等待析构函数。 然后去所有的页面再次关闭所有不可见的对象。 这意味着许多页面可能被换出,并重新多次。

所以,我的回答,使之短是发生因为感人所有内存的结果页面错误的数量造成的所有对象全GC的程序是不可行的,因此程序员必须查看的GC的东西像脚本辅助和数据库工作,但做手工的内存管理正常的事情。

当然,其他的很重要的原因是全局变量。 为了收集知道一个全局变量的指针是在GC它需要特定的关键字,因此现有的C ++代码是行不通的。



Answer 13:

简短的回答:我们不知道如何有效地做到垃圾收集(含有少量的时间和空间开销),并正确地所有的时间(在所有可能的情况下)。

龙答:就像C,C ++是一种系统语言; 这意味着当你正在编写系统的代码,例如,操作系统使用它。 换句话说,C ++的设计,就像C,与最佳性能为主要目标。 语言”标准将不添加任何可能会妨碍性能目标的任何功能。

这可以暂停问题:为什么垃圾收集阻碍性能? 主要的原因是,当涉及到实施,我们[计算机科学家]不知道该怎么做垃圾收集以最小的开销,对于所有的情况。 因此,它是不可能的,C ++编译器和运行时系统,以有效地执行垃圾收集所有的时间。 在另一方面,一个C ++程序员,应该知道他的设计/实施,他的决定如何最好地做垃圾回收的最佳人选。

最后,如果控制(硬件,细节等)及性能(时间,空间,功率等)不是主要的限制,那么C ++不是写工具。 其他语言可能会提供更好的服务,并提供更多的[隐藏]运行时管理,必要的开销。



Answer 14:

当你比较C ++与Java,你可以立即看到C ++不是设计时考虑到隐含垃圾收集,而Java是。

有喜欢的东西在C-风格和确定性析构函数指针的任意不仅GC-实现的降低性能的同时,也会破坏了大量的C ++的向后兼容性 - 遗留代码。

除此之外,C ++是旨在代替运行具有复杂的运行时环境作为独立的可执行文件的语言。

总而言之:是的,它有可能垃圾收集添加到C ++,但对于连续性起见,最好不要这么做。 这样做的成本会比好处更大。



Answer 15:

主要有两个原因:

  1. 因为它并不需要一个(恕我直言)
  2. 因为它几乎不符合RAII,这是C的基石++

C ++已经提供了手动内存管理,堆栈分配,RAII,集装箱,自动指针,智能指针...这应该是足够的。 垃圾收集器是懒惰的程序员不想花5分钟思考关于谁应该拥有哪些对象或者资源应该被释放谁。 这不是我们如何用C做的事情++。



Answer 16:

征收垃圾收集是一个真正的低层次向高层次转变。

如果你看一下字符串与垃圾收集语言处理的方式,你会发现他们只允许高级别字符串操作功能,不允许串二进制访问。 简单地说,所有的字符串函数首先检查指针,看看那里的字符串,即使你只抽出一个字节。 所以,如果你正在做一个循环,在垃圾收集语言处理字符串中的每个字节,它必须计算基站位置以及每个迭代的偏移,因为当字符串移动它无法知道。 然后,你必须想想堆,栈,线程,等等等等。



文章来源: Why doesn't C++ have a garbage collector?